Reputation: 12410
I want to set up joda DateTime
to today at 2 AM (see sample code below). But I'm getting this exception:
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)
What is the correct way to the handle exception above or to create a DateTime
at a particular hour of day?
Sample code:
MutableDateTime now = new MutableDateTime();
now.setHourOfDay(2);
now.setMinuteOfHour(0);
now.setSecondOfMinute(0);
now.setMillisOfSecond(0);
DateTime myDate = now.toDateTime();
Thanks.
Upvotes: 50
Views: 41740
Reputation: 79245
java.time
Shown below is a notice on the Joda-Time Home Page:
Note that from Java SE 8 onwards, users are asked to migrate to
java.time
(JSR-310) - a core part of the JDK which replaces this project.
ZonedDateTime zdt = ZonedDateTime.of(
LocalDate.of(2011, Month.MARCH, 27),
LocalTime.of(2, 0),
ZoneId.of("Europe/Prague") // Replace it as per your requirement
); // printing it outputs 2011-03-27T03:00+02:00[Europe/Prague]
In 2011, DST started on 27 March for European Time when the time was moved forward from 01:59 to 03:00 i.e., on this date, the clock did not show the time 02:00 in your time zone. Check this illustration to learn more.
As you can see in the above example, ZonedDateTime
smartly adjusted the output to 2011-03-27T03:00+02:00[Europe/Prague]
.
Demo:
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/Prague")
);
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 time zone.
// UTC is used as the reference.
Instant instant = zdt.toInstant();
System.out.println(instant);
}
}
Output:
2011-03-27T03:00+02:00[Europe/Prague]
2011-03-27T01:00Z[Etc/UTC]
2011-03-27T01:00:00Z
Learn more about the modern Date-Time API from Trail: Date Time.
Upvotes: 1
Reputation: 11
Update to jodatime 2.1 and use LocalDate.parse()
:
DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy");
LocalDate.parse(date, formatter);
Upvotes: 0
Reputation: 448
If you need to parse date from string:
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();
Worked for me perfectly. Maybe somebody will need it.
Upvotes: 3
Reputation: 5294
I think a lot of the time, you will want joda to fix this automatically for you. You will often not know the correct way to fix a gap date because the size of the gap depends on the zone and the year (although of course it's usually an hour).
One example of this is if you are parsing a timestamp that comes from a source you don't control; eg, the network. If the sender of the timestamp has outdated zone files, this could happen. (If you have outdated zone files, you're pretty much screwed).
Here's a way to do that, which, granted, is slightly more complicated. I've made it work in joda 1.6 as well as 2.x, since we happen to be stuck on 1.6 in our environment.
If you're building a date from some other inputs as in your question, you can start with a UTC date or a LocalDate
as suggested above, and then adapt this to automatically fix your offset. The special sauce is in DateTimeZone.convertLocalToUTC
Dangerous:
public DateTime parse(String str) {
formatter.parseDateTime(gapdate)
}
Safe:
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);
}
I've tested this in the eastern and western hemispheres as well as the Lord Howe Island zone, which happens to have a half hour dst.
It would be kind of nice if joda formatters would support a setStrict(boolean) that would have them take care of this for you...
Upvotes: 8
Reputation: 22894
It seems like you're trying to get from a specific local time to a DateTime
instance and you want that to be robust against daylight savings. Try this... (note I'm in US/Eastern, so our transition date was 13 Mar 11; I had to find the right date to get the exception you got today. Updated my code below for CET, which transitions today.) The insight here is that Joda provides LocalDateTime
to let you reason about a local wall-clock setting and whether it's legal in your timezone or not. In this case, I just add an hour if the time doesn't exist (your application has to decide if this is the right policy.)
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);
}
}
This code produces:
Sure enough, invalid instant due to time zone offset transition! No problem: 2011-03-27T03:00:00.000+02:00
Upvotes: 38
Reputation: 144992
CET switches to DST (summer time) on the last Sunday in March, which happens to be today. The time went from 1:59:59 to 3:00:00 – there's no 2, hence the exception.
You should use UTC instead of local time to avoid this kind of time zone issue.
MutableDateTime now = new MutableDateTime(DateTimeZone.UTC);
Upvotes: 13