0

I need to create a new Java Date based on two strings provided by a user: a date (e.g. "1.1.2015"), and a time of day (e.g. "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?

Markus Yrjölä
  • 859
  • 1
  • 12
  • 25
  • It's not really clear what you mean, to be honest - but are you aware that a `Date` object is *just* a point in time? It doesn't have any concept of a time zone. What is the original `Date` object meant to represent? What would you expect to happen if that `Date` object ended up meaning a different date (i.e. day) for the user than it meant on the server, due to time zones? Where do strings come in? It would help if you could be clearer about the inputs, expected output, and the actual outputs you're getting. – Jon Skeet Jun 03 '15 at 10:50
  • Yes, I am aware that a `Date` doesn't have a time zone. I modified my question to try to make it more clear. – Markus Yrjölä Jun 04 '15 at 07:26
  • 1
    It sounds like *everything* is done in the user's time zone - so why does the server's time zone come into play at all? Note that "midnight in the user's time zone" will not always exist, mind you. It would be much better to parse the date as a `LocalDate` and keep *that* rather than a `Date`. I'd also recommend against using `MutableDateTime`, and if you're already using Joda Time, why use `FastDateFormat` at all? Ideally, use just Joda Time throughout, but if you have to use `Date` at least use Joda Time for everything you can. (We still can't see sample input/output btw... – Jon Skeet Jun 04 '15 at 07:46

1 Answers1

0

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();
Markus Yrjölä
  • 859
  • 1
  • 12
  • 25