我想将 joda
DateTime
设置为今天凌晨 2 点(请参阅下面的示例代码)。但我遇到了这个例外:
Exception in thread "main" org.joda.time.IllegalFieldValueException: Value 2 for hourOfDay is not supported: Illegal instant due to time zone offset transition: 2011-03-27T02:52:05.239 (Europe/Prague)
at org.joda.time.chrono.ZonedChronology$ZonedDateTimeField.set(ZonedChronology.java:469)
at org.joda.time.MutableDateTime.setHourOfDay(MutableDateTime.java:702)
处理上述异常或在一天中的特定时间创建
DateTime
的正确方法是什么?
示例代码:
MutableDateTime now = new MutableDateTime();
now.setHourOfDay(2);
now.setMinuteOfHour(0);
now.setSecondOfMinute(0);
now.setMillisOfSecond(0);
DateTime myDate = now.toDateTime();
谢谢。
您似乎正在尝试从特定的本地时间到
DateTime
实例,并且您希望它能够抵御夏令时。 试试这个...(注意我在美国/东部,所以我们的过渡日期是 2011 年 3 月 13 日;我必须找到正确的日期才能获得今天的例外情况。更新了下面的 CET 代码,该代码今天过渡。 )这里的见解是,Joda 提供了 LocalDateTime
来让您推断本地挂钟设置以及它在您的时区是否合法。 在这种情况下,如果时间不存在,我只需添加一个小时(您的应用程序必须决定这是否是正确的策略。)
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
class TestTz {
public static void main(String[] args)
{
final DateTimeZone dtz = DateTimeZone.forID("CET");
LocalDateTime ldt = new LocalDateTime(dtz)
.withYear(2011)
.withMonthOfYear(3)
.withDayOfMonth(27)
.withHourOfDay(2);
// this is just here to illustrate I'm solving the problem;
// don't need in operational code
try {
DateTime myDateBorken = ldt.toDateTime(dtz);
} catch (IllegalArgumentException iae) {
System.out.println("Sure enough, invalid instant due to time zone offset transition!");
}
if (dtz.isLocalDateTimeGap(ldt)) {
ldt = ldt.withHourOfDay(3);
}
DateTime myDate = ldt.toDateTime(dtz);
System.out.println("No problem: "+myDate);
}
}
此代码产生:
果然,由于时区偏移转换而导致无效瞬间! 没问题:2011-03-27T03:00:00.000+02:00
CET 在三月的最后一个星期日(恰巧是今天)切换到 DST(夏令时)。 时间从 1:59:59 到 3:00:00 – 没有 2,因此例外。
您应该使用 UTC 而不是本地时间,以避免此类时区问题。
MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
我认为很多时候,您会希望 joda 自动为您解决这个问题。 您通常不知道确定间隔日期的正确方法,因为间隔的大小取决于区域和年份(当然通常是一个小时)。
一个例子是,如果您正在解析来自您无法控制的源的时间戳;例如,网络。 如果时间戳的发送者具有过时的区域文件,则可能会发生这种情况。 (如果您有过时的区域文件,那您就完蛋了)。
这里有一种方法可以做到这一点,当然,这种方法稍微复杂一些。 我让它在 joda 1.6 和 2.x 中都可以工作,因为我们的环境恰好停留在 1.6 上。
如果您像问题中那样从其他输入构建日期,则可以按照上面的建议从 UTC 日期或
LocalDate
开始,然后进行调整以自动修复您的偏移量。 特制酱汁在DateTimeZone.convertLocalToUTC
危险:
public DateTime parse(String str) {
formatter.parseDateTime(gapdate)
}
安全:
public DateTime parse(String str) {
// separate date from zone; you may need to adjust the pattern,
// depending on what input formats you support
String[] parts = str.split("(?=[-+])");
String datepart = parts[0];
DateTimeZone zone = (parts.length == 2) ?
DateTimeZone.forID(parts[1]) : formatter.getZone();
// parsing in utc is safe, there are no gaps
// parsing a LocalDate would also be appropriate,
// but joda 1.6 doesn't support that
DateTime utc = formatter.withZone(DateTimeZone.UTC).parseDateTime(datepart);
// false means don't be strict, joda will nudge the result forward by the
// size of the gap. The method is somewhat confusingly named, we're
// actually going from UTC to local
long millis = zone.convertLocalToUTC(utc.getMillis(), false);
return new DateTime(millis, zone);
}
我已经在东半球和西半球以及豪勋爵岛地区进行了测试,该地区恰好有半小时夏令时。
如果 joda 格式化程序支持 setStrict(boolean) ,让他们为你处理这个问题,那就太好了......
如果您需要从字符串中解析日期:
final DateTimeZone dtz = DateTimeZone.getDefault(); //DateTimeZone.forID("Europe/Warsaw")
LocalDateTime ldt = new LocalDateTime("1946-04-14", dtz);
if (dtz.isLocalDateTimeGap(ldt)){
ldt = ldt.plusHours(1);
}
DateTime date = ldt.toDateTime();
Date date = date.toDate();
对我来说非常有效。也许有人会需要它。
更新到jodatime 2.1并使用
LocalDate.parse()
:
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDate.parse(date, formatter);
java.time
以下为Joda-Time主页上的通知:
请注意,从 Java SE 8 开始,用户被要求迁移到
(JSR-310) - JDK 的核心部分,它取代了这个 项目。java.time
ZonedDateTime zdt = ZonedDateTime.of(
LocalDate.of(2011, Month.MARCH, 27),
LocalTime.of(2, 0),
ZoneId.of("Europe/Paris") // Replace it as per your requirement
);
欧洲时间,夏令时于 2011 年 3 月 27 日开始;因此,时钟从 01:59 提前到 03:00。请查看 https://www.timeanddate.com/time/change/france?year=2011 获取插图。因此,
ZonedDateTime
巧妙地将输出调整为上例中的2011-03-27T03:00+02:00[Europe/Paris]
。
演示:
import java.time.*;
public class Main {
public static void main(String[] args) {
ZonedDateTime zdt = ZonedDateTime.of(
LocalDate.of(2011, Month.MARCH, 27),
LocalTime.of(2, 0),
ZoneId.of("Europe/Paris")
);
System.out.println(zdt);
// If you want to convert to a different zone, use withZoneSameInstant
ZonedDateTime zdt2 = zdt.withZoneSameInstant(ZoneId.of("Etc/UTC"));
System.out.println(zdt2);
// However, there is another way to do it for UTC. You can obtain an Instant from
// a ZonedDateTime. An Instant is a point in time independent of any time zone.
// But for practical purposes, we need a reference. UTC is used as the reference.
Instant instant = zdt.toInstant();
System.out.println(instant);
}
}
输出:
2011-03-27T03:00+02:00[Europe/Paris]
2011-03-27T01:00Z[Etc/UTC]
2011-03-27T01:00:00Z
从 Trail:日期时间了解有关现代日期时间 API 的更多信息。