Java 如何使用 SimpleDateFormat 解析多种格式的日期
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4024544/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to parse dates in multiple formats using SimpleDateFormat
提问by Derek
I am trying to parse some dates that are coming out of a document. It would appear users have entered these dates in a similar but not exact format.
我正在尝试解析来自文档的一些日期。看起来用户以类似但不准确的格式输入了这些日期。
here are the formats:
以下是格式:
9/09
9/2009
09/2009
9/1/2009
9-1-2009
What is the best way to go about trying to parse all of these? These seem to be the most common, but I guess what is hanging me up is that if i have a pattern of "M/yyyy" wont that always catch before "MM/yyyy" Do I have to set up my try/catch blocks nested in a least restrictive to most restrictive way? it seems like it sure is going to take a lot of code duplication to get this right.
尝试解析所有这些的最佳方法是什么?这些似乎是最常见的,但我想是什么让我挂了,如果我有一个“M/yyyy”的模式不会总是在“MM/yyyy”之前捕获我是否必须设置我的try/catch块以最少限制到最多限制的方式嵌套?似乎肯定需要大量的代码重复才能做到这一点。
采纳答案by Matt Ball
You'll need to use a different SimpleDateFormat
object for each different pattern. That said, you don't need that many different ones, thanks to this:
您需要SimpleDateFormat
为每个不同的模式使用不同的对象。也就是说,您不需要那么多不同的,多亏了这一点:
Number:For formatting, the number of pattern letters is the minimum number of digits, and shorter numbers are zero-padded to this amount. For parsing, the number of pattern letters is ignored unless it's needed to separate two adjacent fields.
数字:对于格式化,模式字母的数量是最小位数,较短的数字用零填充到这个数量。对于解析,除非需要分隔两个相邻字段,否则将忽略模式字母的数量。
So, you'll need these formats:
所以,你需要这些格式:
"M/y"
(that covers9/09
,9/2009
, and09/2009
)"M/d/y"
(that covers9/1/2009
)"M-d-y"
(that covers9-1-2009
)
"M/y"
(涵盖9/09
、9/2009
、 和09/2009
)"M/d/y"
(涵盖9/1/2009
)"M-d-y"
(涵盖9-1-2009
)
So, my advice would be to write a method that works something like this (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;
}
回答by ChrisR
Matt's approach above is fine, but please be aware that you will run into problems if you use it to differentiate between dates of the format y/M/d
and d/M/y
. For instance, a formatter initialised with y/M/d
will accept a date like 01/01/2009
and give you back a date which is clearly not what you wanted. I fixed the issue as follows, but I have limited time and I'm not happy with the solution for 2 main reasons:
Matt 上面的方法很好,但请注意,如果您使用它来区分格式的日期y/M/d
和d/M/y
. 例如,初始化为的格式化程序y/M/d
将接受类似的日期,01/01/2009
并返回一个显然不是您想要的日期。我解决了以下问题,但我的时间有限,我对解决方案不满意有两个主要原因:
- It violates one of Josh Bloch's quidelines, specifically 'don't use exceptions to handle program flow'.
- I can see the
getDateFormat()
method becoming a bit of a nightmare if you needed it to handle lots of other date formats.
- 它违反了 Josh Bloch 的准则之一,特别是“不要使用异常来处理程序流”。
getDateFormat()
如果您需要它来处理许多其他日期格式,我可以看到该方法变得有点噩梦。
If I had to make something that could handle lots and lots of different date formats and needed to be highly performant, then I think I would use the approach of creating an enum which linked each different date regex to its format. Then use MyEnum.values()
to loop through the enum and test with if(myEnum.getPattern().matches(date))
rather than catching a dateformatexception.
如果我必须制作一些可以处理大量不同日期格式并需要高性能的东西,那么我想我会使用创建一个枚举的方法,将每个不同的日期正则表达式链接到其格式。然后使用MyEnum.values()
循环遍历枚举并使用if(myEnum.getPattern().matches(date))
而不是捕获日期格式异常进行测试。
Anway, that being said, the following can handle dates of the formats 'y/M/d' 'y-M-d' 'y M d' 'd/M/y' 'd-M-y' 'd M y'
and all other variations of those which include time formats as well:
话虽如此,以下内容可以处理格式的日期'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;
}
}
回答by SANN3
回答by locorecto
This solution checks all the possible formats before throwing an exception. This solution is more convenient if you are trying to test for multiple date formats.
此解决方案在抛出异常之前检查所有可能的格式。如果您尝试测试多种日期格式,则此解决方案更方便。
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.");
}
回答by xdjkx
What about just defining multiple patterns? They might come from a config file containing known patterns, hard coded it reads like:
仅仅定义多个模式怎么样?它们可能来自包含已知模式的配置文件,硬编码如下:
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;
回答by Aaron G.
If working in Java 1.8 you can leverage the DateTimeFormatterBuilder
如果在 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;
}
}
See post: Java 8 Date equivalent to Joda's DateTimeFormatterBuilder with multiple parser formats?
请参阅帖子:Java 8 Date 等效于具有多种解析器格式的 Joda 的 DateTimeFormatterBuilder?
回答by Ole V.V.
For the modern answer I am ignoring the requirement to use SimpleDateFormat
. While using this class for parsing was a good idea in 2010 when this question was asked, it is now long outdated. The replacement, DateTimeFormatter
, came out in 2014. The idea in the following is pretty much the same as in the accepted answer.
对于现代答案,我忽略了使用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);
}
This parses each of the input strings from the question into a year-month of 2009-09
. It's important to try the two-digit year first since "M/y"
could also parse 9/09
, but into 0009-09
instead.
这会将问题中的每个输入字符串解析为2009-09
. 首先尝试两位数的年份很重要,因为"M/y"
也可以 parse 9/09
,但0009-09
改为。
A limitation of the above code is it ignores the day-of-month from the strings that have one, like 9/1/2009
. Maybe it's OK as long as most formats have only month and year. To pick it up, we'd have to try LocalDate.parse()
rather then YearMonth.parse()
for the formats that include d
in the pattern string. Surely it can be done.
上面代码的一个限制是它忽略了字符串中的日期,例如9/1/2009
. 只要大多数格式只有月份和年份,也许就可以了。为了找到它,我们必须尝试LocalDate.parse()
而不是模式字符串YearMonth.parse()
中包含的格式d
。当然可以做到。
回答by Vinayak Dornala
Here is the complete example (with main method) which can be added as a utility class in your project. All the format mentioned in SimpleDateFormateAPI is supported in the below method.
这是完整的示例(带有 main 方法),可以将其作为实用程序类添加到您的项目中。下面的方法支持SimpleDateFormateAPI 中提到的所有格式。
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");
}
}
回答by Sairam Asapu
Implemented the same in scala, Please help urself with converting to Java, the core logic and functions used stays the same.
在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) )
}
}
}
}
回答by Rakesh SKadam
Using DateTimeFormatter it can be achieved as below:
使用 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));
}
}
Pay attention at String pattern
, this is the combination of multiple patterns. In open [
and close ]
square brackets you can mention any kind of patterns.
注意String pattern
,这是多个图案的组合。在左方括号[
和右方]
括号中,您可以提及任何类型的模式。