在Mac OS X(Mavericks)上首次发布Java 8(b132)时,使用新java.time package的此代码有效:
String input = "20111203123456";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
渲染:
2011-12-03T12:34:56
但是当我按照DateTimeFormatter class doc中的指定添加“SS”表示秒数(和“55”作为输入)时,会抛出异常:
java.time.format.DateTimeParseException: Text '2011120312345655' could not be parsed at index 0
文档说默认使用严格模式,并且需要与输入数字相同数量的格式字符。所以我很困惑为什么这段代码失败了:
String input = "2011120312345655";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
另一个使用文档示例(“978”)(失败)的例子:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
此示例有效,添加小数点(但我在doc中找不到这样的要求):
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
渲染:
localDateTime: 2011-12-03T12:34:56.978
从输入字符串或中省略句点字符会导致格式失败。
失败:
String input = "20111203123456.978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmssSSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
失败:
String input = "20111203123456978";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern( "yyyyMMddHHmmss.SSS");
LocalDateTime localDateTime = LocalDateTime.parse( input, formatter );
答案 0 :(得分:30)
此问题已在JDK-bug-log中报告。 Stephen Colebourne提到了解决方案:
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmss")
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter();
注意:此解决方法不包括仅两个模式符号SS的用例。调整可能只是使用其他字段,如MICRO_OF_SECOND(6次SSSSSS)或NANO_OF_SECOND(9次SSSSSSSSS)。有关两位数的数字,请参阅下面的更新。
@PeterLawrey关于模式符号的含义" S"见this documentation:
分数:以秒为单位输出纳秒级字段。 纳秒值具有九位数,因此是图案的计数 字母是从1到9.如果它小于9,那么纳秒 值被截断,只有最高位数 输出。在严格模式下解析时,解析的数字必须是 匹配模式字母的数量。在宽松模式下解析时, 解析数字的数量必须至少是模式字母的数量, 最多9位数。
所以我们看到S代表秒的任何一小部分(包括纳秒),而不仅仅是几毫秒。此外,不幸的是,小数部分目前在相邻值解析中表现不佳。
修改强>
作为背景,这里有一些关于相邻值解析的评论。只要字段由小数点或时间部分分隔符(冒号)等文字分隔,对要解析的文本中字段的解释就不难了,因为解析器很容易知道何时停止,即字段部分结束时当下一个字段开始时。因此,如果指定小数点,JSR-310解析器可以处理文本序列。
但是如果你有一系列相邻数字跨越多个领域,那么就会出现一些实施困难。为了让解析器知道字段在文本中何时停止,有必要事先指示解析器给定字段由固定宽度的数字字符表示。这适用于所有appendValue(...)
- 假定数值表示的方法。
不幸的是,JSR-310使用小数部分(appendFraction(...)
)也无法做到这一点。如果你找到关键字"相邻"在类DateTimeFormatterBuilder
的javadoc中,您会发现此功能仅由appendValue(...)
- 方法实现。请注意,模式字母S的规范略有不同,但内部委托给appendFraction()
- 方法。我假设我们至少要等到Java 9(如JDK-bug-log中报告的那样,或者稍后???),直到小数部分也可以管理相邻的值解析。
2015-11-25更新:
以下仅使用两个小数位的代码不起作用并抛出DateTimeParseException
。
DateTimeFormatter dtf =
new DateTimeFormatterBuilder()
.appendPattern("yyyyMMddHHmmssSS")
.appendValue(ChronoField.MILLI_OF_SECOND, 2)
.toFormatter();
String input = "2011120312345655";
LocalDateTime.parse(input, dtf); // abort
解决方法
String input = "2011120312345655";
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSS");
Date d = sdf.parse(input);
System.out.println(d.toInstant()); // 2011-12-03T12:34:56.055Z
不起作用,因为SimpleDateFormat
以错误的方式解释分数(请参阅输出,55 ms而不是550 ms)。
剩下的解决方案是要么长时间等待Java 9(或更高版本?),要么编写自己的hack或使用第三方库作为解决方案。
基于脏黑客的解决方案:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
int len = input.length();
LocalDateTime ldt = LocalDateTime.parse(input.substring(0, len - 2), dtf);
int millis = Integer.parseInt(input.substring(len - 2)) * 10;
ldt = ldt.plus(millis, ChronoUnit.MILLIS);
System.out.println(ldt); // 2011-12-03T12:34:56.550
使用Joda-Time的解决方案:
String input = "2011120312345655";
DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyyMMddHHmmssSS");
System.out.println(dtf.parseLocalDateTime(input)); // 2011-12-03T12:34:56.550
使用我的库Time4J的解决方案:
String input = "2011120312345655";
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern("yyyyMMddHHmmssSS", PatternType.CLDR, Locale.ROOT);
System.out.println(f.parse(input)); // 2011-12-03T12:34:56.550
2016-04-29更新:
正如人们可以通过上面提到的JDK问题看到的那样,它现在被标记为已解决 - 对于 Java 9 。
答案 1 :(得分:0)
这是一种算法,用于调整传统上从格式化日期String
返回的尾随零的顺序。
/**
* Takes a Date and provides the format whilst compensating for the mistaken representation of sub-second values.
* i.e. 2017-04-03-22:46:19.000991 -> 2017-04-03-22:46:19.991000
* @param pDate Defines the Date object to format.
* @param pPrecision Defines number of valid subsecond characters contained in the system's response.
* */
private static final String subFormat(final Date pDate, final SimpleDateFormat pSimpleDateFormat, final int pPrecision) throws ParseException {
// Format as usual.
final String lString = pSimpleDateFormat.format(pDate);
// Count the number of characters.
final String lPattern = pSimpleDateFormat.toLocalizedPattern();
// Find where the SubSeconds are.
final int lStart = lPattern.indexOf('S');
final int lEnd = lPattern.lastIndexOf('S');
// Ensure they're in the expected format.
for(int i = lStart; i <= lEnd; i++) { if(lPattern.charAt(i) != 'S') {
// Throw an Exception; the date has been provided in the wrong format.
throw new ParseException("Unable to process subseconds in the provided form. (" + lPattern + ").", i);
} }
// Calculate the number of Subseconds. (Account for zero indexing.)
final int lNumSubSeconds = (lEnd - lStart) + 1;
// Fetch the original quantity.
String lReplaceString = lString.substring(lStart + (lNumSubSeconds - pPrecision), lStart + lNumSubSeconds);
// Append trailing zeros.
for(int i = 0; i < lNumSubSeconds - pPrecision; i++) { lReplaceString += "0"; }
// Return the String.
return lString.substring(0, lStart) + lReplaceString;
}
答案 2 :(得分:0)
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="0dp"
android:background="@color/colorAccent"
android:elevation="0dp"
android:minHeight="0dp"
android:padding="0dp"
app:collapseIcon="@drawable/logo_mini">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:dividerPadding="100dp"
android:gravity="end"
android:orientation="vertical">
<LinearLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="5dp"
android:paddingLeft="50dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/center_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:layout_weight="0"
android:src="@drawable/logo_mini" />
</LinearLayout>
<ImageView
android:id="@+id/bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:background="@android:color/white"
android:src="@mipmap/mic" />
<TextView
android:id="@+id/emp_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Hi ,"
android:textSize="30sp" />
<TextView
android:id="@+id/textView5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Wellcome back,"
android:visibility="gone" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="here ae you assigned sites"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/dim150"
android:layout_height="@dimen/dim150"
android:layout_below="@+id/home_txt"
android:layout_marginTop="@dimen/dim20"
android:src="@drawable/user_photo"
app:layout_anchor="@id/toolbar"
app:layout_anchorGravity="bottom|center_horizontal" />
</android.support.design.widget.CoordinatorLayout>
这样的事情帮助了我