私はドキュメントから出てくるいくつかの日付を解析しようとしています。ユーザーはこれらの日付を類似の形式で入力したようですが、正確な形式ではありません。
形式は次のとおりです。
9/09
9/2009
09/2009
9/1/2009
9-1-2009
これらすべてを解析しようとする最良の方法は何ですか?これらは最も一般的であるように見えますが、私が困っているのは、「M/yyyy」のパターンがある場合、常に「MM/yyyy」の前にキャッチされることです最も制限の少ない方法から最も制限の多い方法でネストされていますか?これを正しくするためには、多くのコードの複製が必要になるようです。
パターンごとに異なるSimpleDateFormat
オブジェクトを使用する必要があります。つまり、それほど多くのものは必要ありません。 これに感謝 :
Number:フォーマットの場合、パターン文字の数は最小桁数であり、短い数字はこの量までゼロが埋め込まれます。解析では、隣接する2つのフィールドを分離する必要がない限り、パターン文字の数は無視されます。
したがって、これらの形式が必要になります。
"M/y"
(それは9/09
、9/2009
、および09/2009
)"M/d/y"
(それは9/1/2009
)"M-d-y"
(それは9-1-2009
)したがって、私のアドバイスは、次のように機能するメソッドを書くことです(untested):
// ...
List<String> formatStrings = Arrays.asList("M/y", "M/d/y", "M-d-y");
// ...
Date tryParse(String dateString)
{
for (String formatString : formatStrings)
{
try
{
return new SimpleDateFormat(formatString).parse(dateString);
}
catch (ParseException e) {}
}
return null;
}
複数のパターンを定義するだけではどうですか?それらは既知のパターンを含む設定ファイルから来ているかもしれません。
List<SimpleDateFormat> knownPatterns = new ArrayList<SimpleDateFormat>();
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm.ss'Z'"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss"));
knownPatterns.add(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
for (SimpleDateFormat pattern : knownPatterns) {
try {
// Take a try
return new Date(pattern.parse(candidate).getTime());
} catch (ParseException pe) {
// Loop on
}
}
System.err.println("No known Date format found: " + candidate);
return null;
上記のMattのアプローチは問題ありませんが、フォーマット_y/M/d
_と_d/M/y
_の日付を区別するために使用すると問題が発生することに注意してください。たとえば、_y/M/d
_で初期化されたフォーマッタは、_01/01/2009
_のような日付を受け入れ、明らかに意図したものではない日付を返します。次のように問題を修正しましたが、時間に制限があり、2つの主な理由で解決策に満足していません。
getDateFormat()
メソッドは、他の多くの日付形式を処理するために必要な場合、少し悪夢になります。多数の異なる日付形式を処理でき、高いパフォーマンスが必要なものを作成する必要がある場合、異なる日付正規表現をその形式にリンクする列挙型を作成するアプローチを使用すると思います。次に、MyEnum.values()
を使用して列挙型をループし、dateformatexceptionをキャッチするのではなく、if(myEnum.getPattern().matches(date))
でテストします。
とにかく、次のように、形式_'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y'
_の日付と、時刻形式を含むその他のすべてのバリエーションを処理できます。
_import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.util.Date;
public class DateUtil {
private static final String[] timeFormats = {"HH:mm:ss","HH:mm"};
private static final String[] dateSeparators = {"/","-"," "};
private static final String DMY_FORMAT = "dd{sep}MM{sep}yyyy";
private static final String YMD_FORMAT = "yyyy{sep}MM{sep}dd";
private static final String ymd_template = "\\d{4}{sep}\\d{2}{sep}\\d{2}.*";
private static final String dmy_template = "\\d{2}{sep}\\d{2}{sep}\\d{4}.*";
public static Date stringToDate(String input){
Date date = null;
String dateFormat = getDateFormat(input);
if(dateFormat == null){
throw new IllegalArgumentException("Date is not in an accepted format " + input);
}
for(String sep : dateSeparators){
String actualDateFormat = patternForSeparator(dateFormat, sep);
//try first with the time
for(String time : timeFormats){
date = tryParse(input,actualDateFormat + " " + time);
if(date != null){
return date;
}
}
//didn't work, try without the time formats
date = tryParse(input,actualDateFormat);
if(date != null){
return date;
}
}
return date;
}
private static String getDateFormat(String date){
for(String sep : dateSeparators){
String ymdPattern = patternForSeparator(ymd_template, sep);
String dmyPattern = patternForSeparator(dmy_template, sep);
if(date.matches(ymdPattern)){
return YMD_FORMAT;
}
if(date.matches(dmyPattern)){
return DMY_FORMAT;
}
}
return null;
}
private static String patternForSeparator(String template, String sep){
return template.replace("{sep}", sep);
}
private static Date tryParse(String input, String pattern){
try{
return new SimpleDateFormat(pattern).parse(input);
}
catch (ParseException e) {}
return null;
}
}
_
このソリューションは、例外をスローする前に、可能なすべての形式をチェックします。このソリューションは、複数の日付形式をテストする場合に便利です。
Date extractTimestampInput(String strDate){
final List<String> dateFormats = Arrays.asList("yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd");
for(String format: dateFormats){
SimpleDateFormat sdf = new SimpleDateFormat(format);
try{
return sdf.parse(strDate);
} catch (ParseException e) {
//intentionally empty
}
}
throw new IllegalArgumentException("Invalid input for date. Given '"+strDate+"', expecting format yyyy-MM-dd HH:mm:ss.SSS or yyyy-MM-dd.");
}
プロジェクトにユーティリティクラスとして追加できる完全な例(メインメソッド)を示します。 SimpleDateFormate APIに記載されているすべての形式は、以下のメソッドでサポートされています。
import Java.text.ParseException;
import Java.text.SimpleDateFormat;
import Java.util.Date;
import org.Apache.commons.lang.time.DateUtils;
public class DateUtility {
public static Date parseDate(String inputDate) {
Date outputDate = null;
String[] possibleDateFormats =
{
"yyyy.MM.dd G 'at' HH:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"hh 'o''clock' a, zzzz",
"K:mm a, z",
"yyyyy.MMMMM.dd GGG hh:mm aaa",
"EEE, d MMM yyyy HH:mm:ss Z",
"yyMMddHHmmssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
"YYYY-'W'ww-u",
"EEE, dd MMM yyyy HH:mm:ss z",
"EEE, dd MMM yyyy HH:mm zzzz",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSzzzz",
"yyyy-MM-dd'T'HH:mm:sszzzz",
"yyyy-MM-dd'T'HH:mm:ss z",
"yyyy-MM-dd'T'HH:mm:ssz",
"yyyy-MM-dd'T'HH:mm:ss",
"yyyy-MM-dd'T'HHmmss.SSSz",
"yyyy-MM-dd",
"yyyyMMdd",
"dd/MM/yy",
"dd/MM/yyyy"
};
try {
outputDate = DateUtils.parseDate(inputDate, possibleDateFormats);
System.out.println("inputDate ==> " + inputDate + ", outputDate ==> " + outputDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return outputDate;
}
public static String formatDate(Date date, String requiredDateFormat) {
SimpleDateFormat df = new SimpleDateFormat(requiredDateFormat);
String outputDateFormatted = df.format(date);
return outputDateFormatted;
}
public static void main(String[] args) {
DateUtility.parseDate("20181118");
DateUtility.parseDate("2018-11-18");
DateUtility.parseDate("18/11/18");
DateUtility.parseDate("18/11/2018");
DateUtility.parseDate("2018.11.18 AD at 12:08:56 PDT");
System.out.println("");
DateUtility.parseDate("Wed, Nov 18, '18");
DateUtility.parseDate("12:08 PM");
DateUtility.parseDate("12 o'clock PM, Pacific Daylight Time");
DateUtility.parseDate("0:08 PM, PDT");
DateUtility.parseDate("02018.Nov.18 AD 12:08 PM");
System.out.println("");
DateUtility.parseDate("Wed, 18 Nov 2018 12:08:56 -0700");
DateUtility.parseDate("181118120856-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-0700");
DateUtility.parseDate("2018-11-18T12:08:56.235-07:00");
DateUtility.parseDate("2018-W27-3");
}
}
現代の答えでは、SimpleDateFormat
を使用するという要件を無視しています。このクラスを解析に使用することは、この質問が行われた2010年には良いアイデアでしたが、現在では古くなっています。代替のDateTimeFormatter
は2014年に登場しました。以下の考え方は、受け入れられた答えとほとんど同じです。
_private static DateTimeFormatter[] parseFormatters = Stream.of("M/yy", "M/y", "M/d/y", "M-d-y")
.map(DateTimeFormatter::ofPattern)
.toArray(DateTimeFormatter[]::new);
public static YearMonth parseYearMonth(String input) {
for (DateTimeFormatter formatter : parseFormatters) {
try {
return YearMonth.parse(input, formatter);
} catch (DateTimeParseException dtpe) {
// ignore, try next format
}
}
throw new IllegalArgumentException("Could not parse " + input);
}
_
これは、質問からの各入力文字列を_2009-09
_の年月に解析します。 _"M/y"
_は_9/09
_も解析できますが、代わりに_0009-09
_を解析できるため、最初に2桁の年を試すことが重要です。
上記のコードの制限は、_9/1/2009
_のように、ある文字列の曜日を無視することです。ほとんどの形式に月と年しかない場合は問題ありません。パターン文字列にd
を含む形式の場合、LocalDate.parse()
ではなくYearMonth.parse()
を試す必要があります。確かにそれはできます。
Java 1.8で作業している場合、DateTimeFormatterBuilderを活用できます。
public static boolean isTimeStampValid(String inputString)
{
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("" + "[yyyy-MM-dd'T'HH:mm:ss.SSSZ]" + "[yyyy-MM-dd]"));
DateTimeFormatter dateTimeFormatter = dateTimeFormatterBuilder.toFormatter();
try {
dateTimeFormatter.parse(inputString);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
投稿を参照してください: Java 8 Date Jodaの複数のパーサーフォーマットを備えたDateTimeFormatterBuilderに相当?
私は複数の日付形式をjsonに入れていて、ユニバーサル形式でcsvを抽出していました。私は複数の場所を見て、さまざまな方法を試しましたが、最後に次の簡単なコードで変換できます。
private String getDate(String anyDateFormattedString) {
@SuppressWarnings("deprecation")
Date date = new Date(anyDateFormattedString);
SimpleDateFormat dateFormat = new SimpleDateFormat(yourDesiredDateFormat);
String convertedDate = dateFormat.format(date);
return convertedDate;
}
Scalaで同じものを実装しました。Javaへの変換を手伝ってください。使用されるコアロジックと関数は同じままです。
import Java.text.SimpleDateFormat
import org.Apache.commons.lang.time.DateUtils
object MultiDataFormat {
def main(args: Array[String]) {
val dates =Array("2015-10-31","26/12/2015","19-10-2016")
val possibleDateFormats:Array[String] = Array("yyyy-MM-dd","dd/MM/yyyy","dd-MM-yyyy")
val sdf = new SimpleDateFormat("yyyy-MM-dd") //change it as per the requirement
for (date<-dates) {
val outputDate = DateUtils.parseDateStrictly(date, possibleDateFormats)
System.out.println("inputDate ==> " + date + ", outputDate ==> " +outputDate + " " + sdf.format(outputDate) )
}
}
}
DateTimeFormatterを使用すると、次のように実現できます。
import Java.text.SimpleDateFormat;
import Java.time.LocalDateTime;
import Java.time.ZoneOffset;
import Java.time.ZonedDateTime;
import Java.time.format.DateTimeFormatter;
import Java.time.temporal.TemporalAccessor;
import Java.util.Date;
import Java.util.TimeZone;
public class DateTimeFormatTest {
public static void main(String[] args) {
String pattern = "[yyyy-MM-dd[['T'][ ]HH:mm:ss[.SSSSSSSz][.SSS[XXX][X]]]]";
String timeSample = "2018-05-04T13:49:01.7047141Z";
SimpleDateFormat simpleDateFormatter = new SimpleDateFormat("dd/MM/yy HH:mm:ss");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
TemporalAccessor accessor = formatter.parse(timeSample);
ZonedDateTime zTime = LocalDateTime.from(accessor).atZone(ZoneOffset.UTC);
Date date=new Date(zTime.toEpochSecond()*1000);
simpleDateFormatter.setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
System.out.println(simpleDateFormatter.format(date));
}
}
String pattern
、これは複数のパターンの組み合わせです。開いている[
および閉じる]
角カッコあらゆる種類のパターンに言及できます。