Android 上的时间解析问题
Time parsing issue on Android
我在尝试解析时间字符串时遇到解析异常 02:22 p.m.
。
我有以下转换函数:
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
SimpleDateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ROOT);
SimpleDateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ROOT);
Date date = null;
try {
date = originalFormat.parse(datestring);
String formattedDate = targetFormat.format(date);
Date parsedDate = targetFormat.parse(formattedDate);
long nowMilliseconds = parsedDate.getTime();
return nowMilliseconds;
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
该方法在另一个 activity 中以时间格式 "02:22 p.m."
调用。 olddateformat
和 newdateformat
是一样的:hh:mm a
.
它导致日志中出现以下错误:
java.text.ParseException: Unparseable date: "02:22 p.m." (at offset 6)
如何解决这个问题?时间正是上面提到的格式。
我认为 SimpleDateFormat
无法自定义解析 p.m.
部分(它只能识别 AM
或 PM
)。
所以一种替代方法是删除点:
String time = "02:22 p.m.";
SimpleDateFormat format = new SimpleDateFormat("hh:mm a", Locale.ROOT);
date = format.parse(time.replaceAll("\.", ""));
一个细节:要获得 nowMilliseconds
值,您需要所有日期字段 (day/month/year) 和时区。由于这些字段不在输入 String
中,SimpleDateFormat
将它们设置为 January 1st of 1970(以及将秒和毫秒设置为零),并使用系统的默认时区。
我不确定这种获取 1970 年 1 月的行为在所有 Java 版本中是否一致,这是另一个问题,因为您可以根据 environment/device 密码是运行ning。实际上,您可能会得到不同的结果,因为它使用系统的默认时区,并且这可能因环境而异。
如果我在我的机器上 运行 这段代码,它使用我系统的默认时区 (America/Sao_Paulo
),结果是 62520000
。但是如果我将时区更改为另一个时区(比方说,Asia/Kolkata
),结果是 31920000
。您必须了解这种变化并检查这是否是您真正需要的。
另一个细节是,如果 olddateformat
和 newdateformat
相同,则无需创建 2 个不同的格式化程序。
Java的新Date/TimeAPI
旧的 classes(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
在 Android 中您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. You'll also need the ThreeTenABP (more on how to use it ).
所有相关的 class 都在 org.threeten.bp
包中。
有了这个新的 API,您可以使用 org.threeten.bp.format.DateTimeFormatterBuilder
自定义对应于 AM/PM 的文本(因此无需手动删除圆点)。每种情况都有特定的 classes - 在这种情况下,输入只有时间字段(小时和分钟),所以我将使用 org.threeten.bp.LocalTime
class (仅代表时间 - hour/minute/second/nanosecond - 没有日期):
String time = "02:22 p.m.";
// map AM and PM values to strings "a.m." and "p.m."
Map<Long, String> map = new HashMap<Long, String>();
map.put(0L, "a.m.");
map.put(1L, "p.m.");
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// hour and minute
.appendPattern("hh:mm ")
// use custom values for AM/PM
.appendText(ChronoField.AMPM_OF_DAY, map)
// create formatter
.toFormatter(Locale.ROOT);
// parse the time
LocalTime parsedTime = LocalTime.parse(time, fmt);
parsedTime
变量将包含对应于 02:22 PM
的值(只有这个值,它没有日期字段 (day/month/year) 也没有时区)。
要获取毫秒值 (number of milliseconds since 1970-01-01T00:00Z
),您还需要一个日期 (day/month/year) 和一个时区。正如我之前所说,这些字段会影响最终值。
在旧的 API 中,SimpleDateFormat
尝试成为 "smart" 并为这些字段设置默认值(January 1st[= 1970 年的 104=] 在系统的默认时区),但新的 API 对此更加严格,您必须明确说明您想要的日期和时区。
在这个例子中,我使用的是 Asia/Kolkata
时区,但您可以根据需要更改它(更多内容见下文):
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZonedDateTime;
// timezone for Asia/Kolkata
ZoneId zone = ZoneId.of("Asia/Kolkata");
// current date in Kolkata timezone
LocalDate now = LocalDate.now(zone);
// get the parsed time at the specified date, at the specified zone
ZonedDateTime zdt = parsedTime.atDate(now).atZone(zone);
// get the millis value
long millis = zdt.toInstant().toEpochMilli();
如果你想要一个特定的日期而不是当前日期,你可以使用 LocalDate.of(2017, 5, 20)
- 这将得到 May 20th, 2017,例如。有了这个,您可以将上面的代码设置为您需要的日期和时区。
请注意 API 使用 IANA timezones names(始终采用 Region/City
格式,如 America/Sao_Paulo
或 Asia/Kolkata
)。
避免使用 3 个字母的缩写(如 IST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.
获取可用时区列表(并选择最适合您的系统的时区)
如果你想完全模拟 SimpleDateFormat
的作用,你可以使用 LocalDate.of(1970, 1, 1)
并使用 ZoneId.systemDefault()
的默认时区 - 但不推荐这样做,因为系统的默认值可以即使在 运行 时间更改,恕不另行通知。最好明确说明您使用的时区。
或者您可以创建一个始终设置日期默认值(使用 org.threeten.bp.temporal.ChronoField
class)并始终使用相同时区的格式化程序。所以可以直接解析成一个org.threeten.bp.Instant
,得到毫秒值:
String time = "02:22 p.m.";
ZoneId zone = ZoneId.of("Asia/Kolkata");
DateTimeFormatter fmt2 = new DateTimeFormatterBuilder()
// hour and minute
.appendPattern("hh:mm ")
// use custom values for AM/PM (use the same map from previous example)
.appendText(ChronoField.AMPM_OF_DAY, map)
// default value for day: 1
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
// default value for month: January
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
// default value for year: 1970
.parseDefaulting(ChronoField.YEAR, 1970)
// create formatter at my specific timezone
.toFormatter(Locale.ROOT).withZone(zone);
// get the millis value
long millis = Instant.from(fmt2.parse(time)).toEpochMilli();
正好a.m。和 p.m。在盖尔语言环境中就是这样称呼的。至少在我的 Java 8 上。我不确定(所有)Android 手机上是否会出现这种情况,但您可以用它做一些实验。
String datestring = "02:22 p.m.";
Locale parseLocale = Locale.forLanguageTag("ga");
DateTimeFormatter originalFormat = DateTimeFormatter.ofPattern("hh:mm a", parseLocale);
System.out.println(LocalTime.parse(datestring, originalFormat));
这会打印
14:22
雨果如此热情而正确地推荐的是 , I am using the modern Java date and time API, so you will need ThreeTenABP for the above code. See 。或者,您可能想尝试与其他过时的 SimpleDateFormat
.
相同的语言环境
美国西班牙语语言环境在我的 Java 8 上显示相同的行为,因此您也可以尝试一下:Locale.forLanguageTag("es-US")
.
我所做的以下更改对我来说效果很好。
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
DateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ENGLISH);
DateFormat targetFormat = new
SimpleDateFormat(newdateformat,Locale.ENGLISH);
Date date = null;
try {
date = originalFormat.parse(datestring.replaceAll("\.", ""));
String formattedDate = targetFormat.format(date);
Date parsedDate = targetFormat.parse(formattedDate);
long nowMilliseconds = parsedDate.getTime();
return nowMilliseconds;
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
Locale.ENGLISH 你可以使用你的语言环境,英语解决了我的问题。 Reference.
感谢您的回复和参考。
我在尝试解析时间字符串时遇到解析异常 02:22 p.m.
。
我有以下转换函数:
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
SimpleDateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ROOT);
SimpleDateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ROOT);
Date date = null;
try {
date = originalFormat.parse(datestring);
String formattedDate = targetFormat.format(date);
Date parsedDate = targetFormat.parse(formattedDate);
long nowMilliseconds = parsedDate.getTime();
return nowMilliseconds;
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
该方法在另一个 activity 中以时间格式 "02:22 p.m."
调用。 olddateformat
和 newdateformat
是一样的:hh:mm a
.
它导致日志中出现以下错误:
java.text.ParseException: Unparseable date: "02:22 p.m." (at offset 6)
如何解决这个问题?时间正是上面提到的格式。
我认为 SimpleDateFormat
无法自定义解析 p.m.
部分(它只能识别 AM
或 PM
)。
所以一种替代方法是删除点:
String time = "02:22 p.m.";
SimpleDateFormat format = new SimpleDateFormat("hh:mm a", Locale.ROOT);
date = format.parse(time.replaceAll("\.", ""));
一个细节:要获得 nowMilliseconds
值,您需要所有日期字段 (day/month/year) 和时区。由于这些字段不在输入 String
中,SimpleDateFormat
将它们设置为 January 1st of 1970(以及将秒和毫秒设置为零),并使用系统的默认时区。
我不确定这种获取 1970 年 1 月的行为在所有 Java 版本中是否一致,这是另一个问题,因为您可以根据 environment/device 密码是运行ning。实际上,您可能会得到不同的结果,因为它使用系统的默认时区,并且这可能因环境而异。
如果我在我的机器上 运行 这段代码,它使用我系统的默认时区 (America/Sao_Paulo
),结果是 62520000
。但是如果我将时区更改为另一个时区(比方说,Asia/Kolkata
),结果是 31920000
。您必须了解这种变化并检查这是否是您真正需要的。
另一个细节是,如果 olddateformat
和 newdateformat
相同,则无需创建 2 个不同的格式化程序。
Java的新Date/TimeAPI
旧的 classes(Date
、Calendar
和 SimpleDateFormat
)有 lots of problems and design issues,它们正在被新的 APIs.
在 Android 中您可以使用 ThreeTen Backport, a great backport for Java 8's new date/time classes. You'll also need the ThreeTenABP (more on how to use it
所有相关的 class 都在 org.threeten.bp
包中。
有了这个新的 API,您可以使用 org.threeten.bp.format.DateTimeFormatterBuilder
自定义对应于 AM/PM 的文本(因此无需手动删除圆点)。每种情况都有特定的 classes - 在这种情况下,输入只有时间字段(小时和分钟),所以我将使用 org.threeten.bp.LocalTime
class (仅代表时间 - hour/minute/second/nanosecond - 没有日期):
String time = "02:22 p.m.";
// map AM and PM values to strings "a.m." and "p.m."
Map<Long, String> map = new HashMap<Long, String>();
map.put(0L, "a.m.");
map.put(1L, "p.m.");
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// hour and minute
.appendPattern("hh:mm ")
// use custom values for AM/PM
.appendText(ChronoField.AMPM_OF_DAY, map)
// create formatter
.toFormatter(Locale.ROOT);
// parse the time
LocalTime parsedTime = LocalTime.parse(time, fmt);
parsedTime
变量将包含对应于 02:22 PM
的值(只有这个值,它没有日期字段 (day/month/year) 也没有时区)。
要获取毫秒值 (number of milliseconds since 1970-01-01T00:00Z
),您还需要一个日期 (day/month/year) 和一个时区。正如我之前所说,这些字段会影响最终值。
在旧的 API 中,SimpleDateFormat
尝试成为 "smart" 并为这些字段设置默认值(January 1st[= 1970 年的 104=] 在系统的默认时区),但新的 API 对此更加严格,您必须明确说明您想要的日期和时区。
在这个例子中,我使用的是 Asia/Kolkata
时区,但您可以根据需要更改它(更多内容见下文):
import org.threeten.bp.LocalDate;
import org.threeten.bp.ZoneId;
import org.threeten.bp.ZonedDateTime;
// timezone for Asia/Kolkata
ZoneId zone = ZoneId.of("Asia/Kolkata");
// current date in Kolkata timezone
LocalDate now = LocalDate.now(zone);
// get the parsed time at the specified date, at the specified zone
ZonedDateTime zdt = parsedTime.atDate(now).atZone(zone);
// get the millis value
long millis = zdt.toInstant().toEpochMilli();
如果你想要一个特定的日期而不是当前日期,你可以使用 LocalDate.of(2017, 5, 20)
- 这将得到 May 20th, 2017,例如。有了这个,您可以将上面的代码设置为您需要的日期和时区。
请注意 API 使用 IANA timezones names(始终采用 Region/City
格式,如 America/Sao_Paulo
或 Asia/Kolkata
)。
避免使用 3 个字母的缩写(如 IST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.
如果你想完全模拟 SimpleDateFormat
的作用,你可以使用 LocalDate.of(1970, 1, 1)
并使用 ZoneId.systemDefault()
的默认时区 - 但不推荐这样做,因为系统的默认值可以即使在 运行 时间更改,恕不另行通知。最好明确说明您使用的时区。
或者您可以创建一个始终设置日期默认值(使用 org.threeten.bp.temporal.ChronoField
class)并始终使用相同时区的格式化程序。所以可以直接解析成一个org.threeten.bp.Instant
,得到毫秒值:
String time = "02:22 p.m.";
ZoneId zone = ZoneId.of("Asia/Kolkata");
DateTimeFormatter fmt2 = new DateTimeFormatterBuilder()
// hour and minute
.appendPattern("hh:mm ")
// use custom values for AM/PM (use the same map from previous example)
.appendText(ChronoField.AMPM_OF_DAY, map)
// default value for day: 1
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
// default value for month: January
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
// default value for year: 1970
.parseDefaulting(ChronoField.YEAR, 1970)
// create formatter at my specific timezone
.toFormatter(Locale.ROOT).withZone(zone);
// get the millis value
long millis = Instant.from(fmt2.parse(time)).toEpochMilli();
正好a.m。和 p.m。在盖尔语言环境中就是这样称呼的。至少在我的 Java 8 上。我不确定(所有)Android 手机上是否会出现这种情况,但您可以用它做一些实验。
String datestring = "02:22 p.m.";
Locale parseLocale = Locale.forLanguageTag("ga");
DateTimeFormatter originalFormat = DateTimeFormatter.ofPattern("hh:mm a", parseLocale);
System.out.println(LocalTime.parse(datestring, originalFormat));
这会打印
14:22
雨果如此热情而正确地推荐的是 SimpleDateFormat
.
美国西班牙语语言环境在我的 Java 8 上显示相同的行为,因此您也可以尝试一下:Locale.forLanguageTag("es-US")
.
我所做的以下更改对我来说效果很好。
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
DateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ENGLISH);
DateFormat targetFormat = new
SimpleDateFormat(newdateformat,Locale.ENGLISH);
Date date = null;
try {
date = originalFormat.parse(datestring.replaceAll("\.", ""));
String formattedDate = targetFormat.format(date);
Date parsedDate = targetFormat.parse(formattedDate);
long nowMilliseconds = parsedDate.getTime();
return nowMilliseconds;
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
Locale.ENGLISH 你可以使用你的语言环境,英语解决了我的问题。 Reference.
感谢您的回复和参考。