我认为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 将它们设置为 January 1st of 1970(并将秒和毫秒设置为零),并且使用系统的默认时区。
我不确定这种获取 1970 年 1 月的行为在所有 Java 版本中是否一致,这是另一个问题,因为您可以根据代码运行的环境/设备获取不同的值。实际上,您可能会得到不同的结果,因为它使用系统的默认时区,并且在不同的环境中可能会有所不同。
如果我在我的机器上运行这段代码,它会使用我系统的默认时区 (America/Sao_Paulo),结果是62520000。但是,如果我将时区更改为另一个时区(比如说,Asia/Kolkata),结果是31920000。您必须了解这种变化并检查这是否是您真正需要的。
另一个细节是,如果 olddateformat 和 newdateformat 相同,则无需创建 2 个不同的格式化程序。
Java 的新日期/时间 API
旧的类(Date、Calendar 和 SimpleDateFormat)有 lots of problems 和 design issues,它们正在被新的 API 取代。
在 Android 中,您可以使用 ThreeTen Backport,这是 Java 8 新日期/时间类的一个很好的反向移植。你还需要ThreeTenABP(更多关于如何使用它here)。
所有相关类都在org.threeten.bp 包中。
借助这个新 API,您可以使用 org.threeten.bp.format.DateTimeFormatterBuilder 自定义对应于上午/下午的文本(因此无需手动删除点)。每种情况都有特定的类 - 在这种情况下,输入只有时间字段(小时和分钟),所以我将使用 org.threeten.bp.LocalTime 类(它只代表一个时间 - 小时/分钟/秒/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 的值(只有这个值,它没有日期字段(日/月/年)也没有时区)。
要获取毫秒值 (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) - 例如,这将得到 2017 年 5 月 20 日th。有了这个,您可以将上面的代码设置为您需要的日期和时区。
请注意,API 使用IANA timezones names(始终采用Region/City 格式,如America/Sao_Paulo 或Asia/Kolkata)。
避免使用三个字母的缩写(如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();