1

I have two dates with time values. I need to combine date from one and time from another and make third date and time of those.

Here is the JSON:

  "block_times": [
    {
      "id": 63672,
      "name": "One hour blocked time",
      "all_day": false,
      "start_date": "07/07/2020",
      "end_date": "07/07/2020",
      "start_time": "2000-01-01T16:00:00.000-06:00",
      "end_time": "2000-01-01T17:00:00.000-06:00",
      "note": "One hour block time",
      "account_id": 1,
      "service_routes_id": 4502,
      "created_at": "2020-07-07T10:50:30.599-05:00",
      "updated_at": "2020-07-07T10:50:30.599-05:00"
    }
  ]

I need to get date from start_date and I need to get time from start_time and combine them together with date and time value.

I have the following code:

 public static Date convertBlockedTimeToUserTime(String startTimeStr, String startDateStr) {
      SimpleDateFormat dateFormat = new SimpleDateFormat(getDateTimeFormat(), Locale.US);
      SimpleDateFormat startDateFormatInitial = new SimpleDateFormat("MM/dd/yyyy", Locale.US);
      SimpleDateFormat startDateFormatFinal = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
      SimpleDateFormat startTimeFormat = new SimpleDateFormat(getTimeFormat(), Locale.US);
    try {
      String resultStartDate = startDateFormatFinal.format(startDateFormatInitial.parse(startDateStr));
      String resultStartTime = startTimeFormat.format(dateFormat.parse(startTimeStr));
      String result =  resultStartDate+"T"+resultStartTime;
      return dateFormat.parse(result);
    } catch (ParseException e) {
      e.printStackTrace();
      return null;
    }

I get this result: 2020-07-07T11:00:00.000+01:00 and this is wrong since my current time zone is GMT +2, so it should be like this: 2020-07-07T10:00:00.000+02:00.

Can anyone help me to find the problem?

Zookey
  • 2,637
  • 13
  • 46
  • 80
  • 1
    First problem is the use of `java.util` instead of `java.time` and the second one is the presence of full datetimes including an offset where you obviously only need the time part... and maybe the offset, but that's unclear. – deHaar Jul 08 '20 at 12:19
  • As an aside consider throwing away the long outmoded and notoriously troublesome `SimpleDateFormat` and friends. See if you either can use [desugaring](https://developer.android.com/studio/write/java8-support-table) or add [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project, in order to use java.time, the modern Java date and time API. It is so much nicer to work with. – Ole V.V. Jul 08 '20 at 17:58
  • Related: (1) [How to combine date and time into a single object?](https://stackoverflow.com/questions/43752557/how-to-combine-date-and-time-into-a-single-object) (2) [How do I combine date from one timestamp and time from another timestamp?](https://stackoverflow.com/questions/31991242/how-do-i-combine-date-from-one-timestamp-and-time-from-another-timestamp) – Ole V.V. Jul 08 '20 at 18:06

2 Answers2

3

Your API is extremely clunky. As a rule, the java.time package (which, if you're on an ancient java version that doesn't have it, can still be obtained via the backport) is very good at giving you all sorts of types that can represent all sorts of crazy takes on dates and times, and by using the right types to represent things as they are, code works out as a consequence. Time is a very hard concept to get right, so use the best tool available.

import java.time.*;
import java.time.format.*;

class Example {
    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("dd/MM/yyyy");

    public static void main(String[] args) {
        String inDate = "07/07/2020";
        String inTime = "2000-01-01T16:00:00.000-06:00";
        LocalDate ld = LocalDate.parse(inDate, DATE_FORMAT);
        OffsetTime time = OffsetDateTime.parse(inTime, DateTimeFormatter.ISO_OFFSET_DATE_TIME).toOffsetTime();
        ZonedDateTime target = time.atDate(ld).atZoneSameInstant(ZoneId.systemDefault());
        System.out.println(target);
    }
}

Here we convert your crazy time input first into an OffsetDateTime (because that is what it is; it a full date and time with an offset and not a time zone; a time zone is something like 'amsterdam'; real time zones have different offsets depending on the time of year and can be entirely different depending on century and such. Offsets are what they say they are). We then discard the date part. Then we turn it back into an offset date time by mixing in the date from your date input, and finally we 're-zone' this thing to your preferred zone. In this snippet, I chose 'the system default', which may not be appropriate for a server app, but is probably what you want on android.

Note that this API is inherently confusing. What happens if, say, the inputs are as follows:

        String inDate = "07/07/2020";
        String inTime = "2000-01-01T23:00:00.000-06:00";

Do you then want this output if you were located in Europe, in summer:

2020-07-08T07:00:00.000+02:00

note how that's not actually on the day the input said, because that's how the zones work out.

That's what this snippet does. It's bizarre if you wouldn't want this (after all, it means that you adjust by a full day depending on where your phone's at when the code runs, that sounds bizarre, no?) - but if the zoned date time result must fall on the input date, then you would probably be best off checking at the end if the day is 'wrong' and then use .plusDays or .minusDays to adjust.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thanks for the clarification and the answer. Unfortunately, I am using Android SDK which is a very limited and older version of Android API that can not use Java Time features that were introduced with Java 8. That orginal date and time with GMT-6 is from USA and in that format is saved in the API database, which I do not control. I am from Europe and I need to display in GMT + 2 time zone. `2020-07-07T07:22:00.000+02:00` should be the answer, per my understanding. – Zookey Jul 08 '20 at 13:10
  • @Zookey From what I have been informed there seem to be two options for you: (1) Using java.time through [desugaring](https://developer.android.com/studio/write/java8-support-table). (2) Using the backport of java.time; add [ThreeTenABP](https://github.com/JakeWharton/ThreeTenABP) to your Android project – Ole V.V. Jul 08 '20 at 18:03
  • @Zookey I linked to the backport. It works fine on android. – rzwitserloot Jul 09 '20 at 11:08
-1
  "start_date": "07/07/2020",
 "end_date": "07/07/2020",
 "start_time": "2000-01-01T16:00:00.000-06:00",
 "end_time": "2000-01-01T17:00:00.000-06:00",

What a clunky api

Anyways, as usual, you can use the Calendar API to create a date that refers to both.

  SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy", Locale.US);
  SimpleDateFormat timeFormat = new SimpleDateFormat(getDateTimeFormat(), Locale.US); // TODO: parse 2000-01-01T16:00:00.000-06:00

  Date date = dateFormat.parse(startDateString);
  Date time = timeFormat.parse(startTimeString);

  Calendar dateCalendar = Calendar.getInstance();
  dateCalendar.setTimeInMillis(date.getTime());
  
  Calendar timeCalendar = Calendar.getInstance();
  timeCalendar.setTimeInMillis(time.getTime());

  int year = dateCalendar.get(Calendar.YEAR);
  int month = dateCalendar.get(Calendar.MONTH);
  int day = dateCalendar.get(Calendar.DAY_OF_MONTH);

  timeCalendar.set(Calendar.DAY_OF_MONTH, 1);
  timeCalendar.set(Calendar.YEAR, year);
  timeCalendar.set(Calendar.MONTH, month);
  timeCalendar.set(Calendar.DAY_OF_MONTH, day);

  return timeCalendar.getTime();
  

NOTE: there's a chance that parsing the timezone info into java.util.Date is not a good idea, in which case you might want to look into using the java.time.* API, that should be available in AS 4.0 with AGP 4.0.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • I think geneva has something to say about recommending (or even insinuating a recommendation) to use the Calendar API. – rzwitserloot Jul 08 '20 at 12:18
  • This is Android and I'm not sure if desugaring for `java.time` works yet, so ¯\\_(ツ)_/¯ if you want to provide an answer using ThreeTenBP or something, then you can definitely do that. – EpicPandaForce Jul 08 '20 at 12:23
  • As usual, not using 310 results in pernicious problems. This code will fail in utterly bizarre ways for certain dates. For example, if the input time is '23:00:00.000-06:00' and the desired date is the 7th of juli, and the phone is in amsterdam, what output is desired? your snippet would say: 7 in the morning on the 7th. Which is wrong; it'd be 7 in the morning on the 8th. This is why recommending Calendar is so bad: Time is hard, and Calendar makes you write wrong code. – rzwitserloot Jul 08 '20 at 12:36
  • (Or alternatively OP really really does want this, but this weirdness is inherent in the question, but not obvious. Using the right types from 310 tends to produce either the less 'wonky' result (which would be: the answer is 07:00 on the 8th of july), or will flat out not compile, nicely matching that the question as asked isn't properly stated. In this case you get the 'lesser' result (of the least wonky answer), but the flow of converting to an OffsetDT and then re-zoning that highlights far better that you're first finding the right instant, then zoning that in a desired zone. – rzwitserloot Jul 08 '20 at 12:38
  • I hear this approach actually breaks timezone info, but I think that happened during the parsing of `Date`. – EpicPandaForce Jul 08 '20 at 13:36