简体   繁体   中英

How to change a Date based on time-only input and account for time zone and DST?

I need to create a new Java Date based on two strings provided by a user: a date (eg "1.1.2015"), and a time of day (eg "23:00"). First the user enters the date, which is sent to the server and parsed into a Date (time of day is set to midnight in the user's time zone). After this, the user enters the time of day, which is sent to the server, and a new Date needs to be created, combining the date from the first Date instance and time of day from the new user input.

Example: Say the server's time zone is UTC, and the user's time zone is UTC-2. The user enters "1.1.2015" into the date field, which is interpreted in the server as 2:00 1.1.2015 UTC (1st of January at 2:00 AM in UTC, which is midnight in the user's time zone). The user then enters "23:00" into the time field (24-hour clock). This needs to be interpreted in the server as 1:00 2.1.2015 UTC (2nd of January at 1:00 AM).

We use Apache Commons FastDateFormat for transforming strings to Dates and vice versa, and Joda Time for date manipulation. The result needs to be a plain old Java Date. I've tried to combine the existing Date instance and the time of day input from the user like this:

Date datePart= ...; // The date parsed from the first user input
FastDateFormat timeFormat = ...;
DateTimeZone userTimeZone = DateTimeZone.forTimeZone(timeFormat.getTimeZone());
String userTimeInput = ...; // The time of day from the user

MutableDateTime dateTime = new MutableDateTime(datePart, DateTimeZone.UTC);
Date newTime = timeFormat.parse(userTimeInput);
dateTime.setTime(new DateTime(newTime, DateTimeZone.UTC));

// Determine if the date part needs to be changed due to time zone adjustment
long timeZoneOffset = userTimeZone.getOffset(dateTime);
long newMillisOfDay = dateTime.getMillisOfDay();
if (newMillisOfDay + timeZoneOffset > 24 * 60 * 60 * 1000) {
    dateTime.addDays(-1);
} else if (newMillisOfDay + timeZoneOffset < 0) {
    dateTime.addDays(1);
}

Date newServerDate = dateTime.toDate();

Changing the time of day of an existing Date like this is a bit problematic. The above doesn't work; if the user changes the time of day multiple times, the +/-1 day adjustment is potentially made every time. Also, the above code doesn't take DST into account. If datePart is in DST, the times entered by our example user should be treated as being in UTC-1. When using FastDateFormat and only parsing the time of day, the date is set to the epoch, meaning that the time entered by the user will always be treated as being in UTC-2. This will cause a one hour offset in the result.

How to adjust the Date in the server based on the given time of day and properly take the time zone and DST into account?

I solved this by using the suggestions by Jon in the comments. I still have to end up with a Date , so I couldn't start using Joda Time for everything. I did however move away from FastDateFormat and MutableDateTime for this particular use case. Thanks for the tips! The solution looks like this:

Date datePart= ...;           // The date parsed from the first user input
String userTimeInput = ...;   // The time of day from the user
Locale userLocale = ...;
DateTimeZone userTimeZone = ...;

DateTime dateInUserTimeZone = new DateTime(datePart, userTimeZone);
DateTimeFormatter formatter = DateTimeFormat.shortTime().withLocale(userLocale);
LocalTime time = formatter.parseLocalTime(userTimeInput);

Date newDate = dateInUserTimeZone.withTime(time.getHourOfDay(), time.getMinuteOfHour(),
        time.getSecondOfMinute(), time.getMillisOfSecond()).toDate();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM