尝试解析时间字符串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;
}
}
在另一个时间格式为"02:22 p.m."
的活动中调用该方法。 olddateformat
和newdateformat
相同:hh:mm a
。
它会在日志中导致以下错误:
java.text.ParseException:Unparseable date:“02:22 p.m.” (抵消6)
如何解决此问题?时间恰好是上面提到的格式。
答案 0 :(得分:3)
在上午和下午发生这种情况。在盖尔语言环境中被称为这个。至少在我的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分
正如Hugo如此热烈而正确地推荐his answer,我正在使用现代Java日期和时间API,因此您需要ThreeTenABP才能获得上述代码。见How to use ThreeTenABP in Android Project。或者,您可能希望使用已过时的SimpleDateFormat
尝试使用相同的区域设置。
美国西班牙语语言环境在我的Java 8上显示相同的行为,因此您也可以尝试:Locale.forLanguageTag("es-US")
。
答案 1 :(得分:2)
我认为无法自定义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
值,您需要所有日期字段(日/月/年)和时区。由于这些字段不在输入String
中,SimpleDateFormat
将它们设置为 1970年1月1日 st (并且还将秒和毫秒设置为零),并使用系统的默认时区。
我不确定1970年1月获得的这种行为是否在所有Java版本中都是一致的,这是另一个问题,因为根据代码运行的环境/设备,您可以获得不同的值。实际上,您可能会有不同的结果,因为它使用系统的默认时区,这可能因不同的环境而异。
如果我在我的机器上运行此代码,它将使用我系统的默认时区(America/Sao_Paulo
),结果为62520000
。但是,如果我将时区更改为另一个(例如,Asia/Kolkata
),则结果为31920000
。您必须了解这种变化,并检查这是否是您真正需要的。
另一个细节是,如果olddateformat
和newdateformat
相同,则无需创建2个不同的格式化程序。
旧类(Date
,Calendar
和SimpleDateFormat
)有lots of problems和design issues,它们将被新API取代。< / p>
在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。您还需要ThreeTenABP(更多关于如何使用它here)。
所有相关课程都在org.threeten.bp
包中。
使用此新API,您可以使用org.threeten.bp.format.DateTimeFormatterBuilder
自定义与AM / PM对应的文本(因此无需手动删除点)。每个案例都有特定的类 - 在这种情况下,输入只有时间字段(小时和分钟),因此我将使用org.threeten.bp.LocalTime
类(仅代表时间 - 小时/分钟) /秒/纳秒 - 没有日期):
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
对应的值(并且只有此值,它没有日期字段(日/月/年)或时区)。
要获得毫秒值(number of milliseconds since 1970-01-01T00:00Z
),您还需要一个日期(日/月/年)和时区。正如我之前所说,这些字段会影响最终值。
在旧API中,SimpleDateFormat
尝试“智能”并为系统默认设置的那些字段设置默认值( 1970年1月1日 st 时区),但新的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)
- 例如,这将获得 5月20日 th ,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
类)并始终使用相同的时区。因此,您可以直接将其解析为org.threeten.bp.Instant
并获取millis值:
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();