我有一个包含原始日期字段(存储为字符数据)的数据库字段,例如
Friday, September 26, 2008 8:30 PM Eastern Daylight Time
我可以使用SimpleDateFormat轻松将其解析为Date
1 2
| DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate); |
我想做的是从此字符串中提取一个TimeZone对象。 运行此应用程序的JVM中的默认TimeZone是GMT,所以我不能使用上面解析的Date中的.getTimezoneOffset()(因为它将返回默认的TimeZone)。
除了标记原始字符串并找到时区字符串的开始位置(因为我知道格式将始终为EEEE, MMMM dd, yyyy hh:mm aa zzzz)之外,还有一种方法可以使用DateFormat / SimpleDateFormat / Date / Calendar API提取TimeZone对象- 与我用DateFormat.parse()解析过的String相同的TimeZone?
关于Java API中的Date与Calendar的一件令我烦恼的事情是,应该在所有位置替换Date ...但是他们决定,哦,还是让我们继续使用Date 在DateFormat类中。
我发现以下内容:
1 2 3 4 5 6 7 8 9
| DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
Date scheduledDate = dbFormatter.parse("Friday, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(scheduledDate);
System.out.println(dbFormatter.format(scheduledDate));
TimeZone tz = dbFormatter.getTimeZone();
System.out.println(tz.getDisplayName());
dbFormatter.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
System.out.println(dbFormatter.format(scheduledDate)); |
产生以下内容:
1 2 3 4
| Fri Sep 26 20:30:00 CDT 2008
Friday, September 26, 2008 08:30 PM Eastern Standard Time
Eastern Standard Time
Friday, September 26, 2008 08:30 PM Central Daylight Time |
我实际上发现这有些令人惊讶。但是,我想这表明您的问题的答案是在解析后仅在格式化程序上调用getTimeZone。
编辑:
以上是与Sun的JDK 1.6一起运行的。
TL;博士
1 2 3 4
| ZonedDateTime.parse(
"Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ,
DateTimeFormatter.ofPattern("EEEE, MMMM d, uuuu h:m a zzzz" )
).getZone() |
java.time
现代方法是使用java.time类。"问题"和"其他答案"使用麻烦的旧旧日期时间类或Joda-Time项目,现在这两个类都已被java.time类取代。
用格式模式定义DateTimeFormatter对象以匹配您的数据。
1
| DateTimeFormatter f = DateTimeFormatter.ofPattern("EEEE, MMMM d, uuuu h:m a zzzz" ); |
分配Locale可以指定日期名称和月份名称的人类语言,以及其他格式问题的文化规范。
1
| f = f.withLocale( Locale.US ); |
最后,进行解析以获得ZonedDateTime对象。
1 2
| String input ="Friday, September 26, 2008 8:30 PM Eastern Daylight Time" ;
ZonedDateTime zdt = ZonedDateTime.parse( input , f ); |
zdt.toString(): 2008-09-26T20:30-04:00[America/New_York]
您可以从ZonedDateTime(表示为ZoneId对象)中请求时区。如果需要有关时区的更多信息,则可以查询ZoneId。
1
| ZoneId z = zdt.getZone(); |
在IdeOne.com上自行查看。
ISO 8601
避免以这种糟糕的格式交换日期时间数据。不要假设英语,不要使用诸如" day-of-day"之类的名称来为输出添加内容,也不要使用诸如Eastern Daylight Time之类的伪时区。
对于时区:以continent/region格式指定正确的时区名称,例如America/Montreal,Africa/Casablanca或Pacific/Auckland。切勿使用3-4个字母的缩写,例如EST或IST,因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。
要将日期时间值序列化为文本,请仅使用ISO 8601格式。解析/生成字符串以表示其值时,java.time类默认使用这些格式。
关于java.time
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧式传统日期时间类,例如java.util.Date,Calendar和SimpleDateFormat。
现在处于维护模式的Joda-Time项目建议迁移到java.time。
要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
在哪里获取java.time类?
-
Java SE 8和SE 9及更高版本
-
内置。
-
标准Java API的一部分,具有捆绑的实现。
-
Java 9添加了一些次要功能和修复。
-
Java SE 6和SE 7
-
java.time的许多功能在ThreeTen-Backport中都被反向移植到Java 6和7。
-
Android的
-
ThreeTenABP项目专门针对Android改编了ThreeTen-Backport(如上所述)。
-
请参阅如何使用…。
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval,YearWeek,YearQuarter等。
我建议您查看Joda Time日期和时间API。我最近被转换为对此的信奉者,因为它往往比Java中对日期和时间的内置支持要优越得多。特别是,您应该检出DateTimeZone类。希望这可以帮助。
http://joda-time.sourceforge.net/
http://joda-time.sourceforge.net/api-release/index.html
@Ed Thomas:
我尝试了与您的示例非常相似的方法,但结果却截然不同:
1 2 3 4 5 6 7 8 9 10 11 12
| String testString ="Friday, September 26, 2008 8:30 PM Pacific Standard Time";
DateFormat df = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println("The default TimeZone is:" + TimeZone.getDefault().getDisplayName());
System.out.println("DateFormat timezone before parse:" + df.getTimeZone().getDisplayName());
Date date = df.parse(testString);
System.out.println("Parsed [" + testString +"] to Date:" + date);
System.out.println("DateFormat timezone after parse:" + df.getTimeZone().getDisplayName()); |
输出:
The default TimeZone is: Eastern Standard Time
DateFormat timezone before parse: Eastern Standard Time
Parsed [Friday, September 26, 2008 8:30 PM Pacific Standard Time] to Date: Sat Sep 27 00:30:00 EDT 2008
DateFormat timezone after parse: Eastern Standard Time
似乎DateFormat.getTimeZone()在parse()之前和之后返回相同的TimeZone ...,即使我在调用parse()之前抛出了明确的setTimeZone()。
查看DateFormat和SimpleDateFormat的源代码,似乎getTimeZone()只是返回基础Calendar的TimeZone ...,它将默认为默认Locale / TimeZone的Calendar,除非您指定要使用的特定日历。
埃德说得对。您需要在解析时间后在DateFormat对象上使用timeZone。
1 2 3 4 5 6 7
| String rawDate ="Friday, September 26, 2008 8:30 PM Eastern Daylight Time";
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
Date scheduledDate = dbFormatter.parse(rawDate);
System.out.println(rawDate);
System.out.println(scheduledDate);
System.out.println(dbFormatter.getTimeZone().getDisplayName()); |
产生
1 2 3
| Friday, September 26, 2008 8:30 PM Eastern Daylight Time
Fri Sep 26 20:30:00 CDT 2008
Eastern Standard Time |
日期和日历之间的主要区别在于,日期只是一个值对象,没有修改它的方法。因此,它旨在将日期/时间信息存储在某个地方。如果使用Calendar对象,则可以在将其设置为使用日期/时间信息执行某些业务逻辑的持久实体后,对其进行修改。这非常危险,因为实体无法识别此更改。
Calendar类设计用于按日期/时间进行操作,例如添加天数之类。
玩一下您的示例,我得到以下信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class TimeZoneExtracter {
public static final void main(String[] args) throws ParseException {
DateFormat dbFormatter = new SimpleDateFormat("EEEE, MMMM dd, yyyy hh:mm aa zzzz");
System.out.println(dbFormatter.getTimeZone());
dbFormatter.parse("Fr, September 26, 2008 8:30 PM Eastern Daylight Time");
System.out.println(dbFormatter.getTimeZone());
}
} |
输出:
sun.util.calendar.ZoneInfo[id="Europe/Berlin"...
sun.util.calendar.ZoneInfo[id="Africa/Addis_Ababa"...
这是您想要的结果吗?
作为部分解决方案,您可以使用RegEx匹配来获取时区,因为您之前总是会有相同的文本。上午或下午。
我对Java时区了解不足,无法完全了解它。