-2

I am facing some problems with Date and SimpleDateFormat objects in Java. I know that I need to switch them with new Date and Time API in Java 8, and I want to do that but I have one issue. When I retrieve my date from database(it is SimpleDateFormat and I cant change it), and when I want to add some hours(for example 4 PM), and then that result I need to be in time zone which user selected from the dropdown(Lets say America/Caracas), so I want that result be 16h in America/Caracas but in my case is 16h UTC + difference between UTC and America/Caracas. This is my code:

Date startDate -> it's value is Tue Feb 23 20:00:00 CET 2021
Date date = new SimpleDateFormat("yyy-MM-dd").format( startDate + 4:00PM + America/Caracas);

But, weird thing is that this formatting ads that 4 PM value but somehow ignores that part with timezone. So, instead I have 20h in America/Caracas I am getting 20h in UTC. Can someone helps me?

  • 4
    Use `java.time` for this as long as you aren't forced to use the outdated and troublesome API from `java.util` and `java.text`. – deHaar Feb 23 '21 at 18:13
  • 3
    `format( startDate + 4:00PM + America/Caracas)` is an *impossibility*, since the parameter to `format()` is a `java.util.Date` and **`Date` objects don't have time zones**. It is not ignoring the time zone, because there is no time zone there *to* ignore. --- If you want to specify a time zone, call [`dateFormat.setTimeZone(TimeZone.getTimeZone("America/Caracas"))`](https://docs.oracle.com/javase/8/docs/api/java/text/DateFormat.html#setTimeZone-java.util.TimeZone-). – Andreas Feb 23 '21 at 18:52
  • 1
    Your code gives me `The operator + is undefined for the argument type(s) Date, int`. Could you please [create a Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example)? [I downvoted because lacking an MCVE makes it hard to answer](http://idownvotedbecau.se/nomcve/). – Ole V.V. Feb 23 '21 at 20:02
  • 2
    There is no such thing as storing a date as `SimpleDateFormat` in your datebase. You are probably using your database’s `date`data type (I sure hope) and can thus retrieve your date as a `LocalDate` into Java. [Insert & fetch java.time.LocalDate objects to/from an SQL database such as H2](https://stackoverflow.com/questions/43039614/insert-fetch-java-time-localdate-objects-to-from-an-sql-database-such-as-h2). – Ole V.V. Feb 23 '21 at 20:07
  • *I know that I need to switch them with new Date and Time API in Java 8, and I want to do that …* Applauding. – Ole V.V. Feb 23 '21 at 20:08
  • I disagree about the duplicate marking. This question may be related: [TimeZone problem in Java](https://stackoverflow.com/questions/1812700/timezone-problem-in-java) (no duplicate either). – Ole V.V. Feb 24 '21 at 06:55
  • Voting to re-open. The alleged duplicate: (a) asks for a name or source of a format. This question does not. (b) asked about standard ISO 8601 formatted string for a date with time-of-day with offset of zero, YYYY-MM-DDTHH:MM:SS.SSSSSSSSSZ. Nowhere in this question is that format mentioned. – Basil Bourque Feb 24 '21 at 16:07
  • More reasons to reopen: (c) [The alleged duplicate](https://stackoverflow.com/questions/8405087/what-is-this-date-format-2011-08) asks about the `T` in the middle. This question has no `T` to explain. (d) This Question asks about adding four hours and/or adjusting between time zone. The alleged duplicate involves neither of those. – Basil Bourque Feb 24 '21 at 16:32
  • 1
    @Atenica I suggest you edit you Question to be more clear about what you want with regard to adding four hours or setting the time to 4:00. I can fine-tune my Answer if you provide clarity. – Basil Bourque Feb 24 '21 at 16:35

2 Answers2

5

java.time

You said:

need to switch them with new Date and Time API in Java 8

Yes, absolutely. The legacy Date, Calendar, and SimpleDateFormat classes are terrible: confusing and flawed.

You said:

When I retrieve my date from database(it is SimpleDateFormat and I cant change it)

If your date-time value is properly stored in the database in a date-time column, it does not have a "format" because it is not text.

Date-time values stored in date-time columns should be retrieved as date-time objects, not text. If you are receiving text, you should back-track up the food chain of data to fix that problem.

Zoned

If stored in a column of a type akin to the SQL-standard TIMESTAMP WITH TIME ZONE, retrieve as a OffsetDateTime.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

You said:

I want to add some hours(for example 4 PM)

That is a contradiction. Do you want to add four hours?

OffsetDateTime odtFourHoursLater = odt.plusHours( 4 ) ;

Or do you want to set the time-of-day to 4 PM?

OffsetDateTime odtSetToFourPm = odt.with( LocalTime.of ( 16 , 0 ) ) ;

You said:

that result I need to be in time zone which user selected from the dropdown(Lets say America/Caracas),

The OffsetDateTime class supported by JDBC 4.2 represents a moment as seen through an offset-from-UTC, a number of hours-minutes-seconds. Most databases will deliver this object to you with an offset of zero hours-minutes-seconds.

Understand that a time zone is not an offset. A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region. A time zone has a name in the format of Continent/Region.

To represent a moment as seen in a time zone rather than an offset, use ZonedDateTime class.

ZoneId zoneId = ZoneId.of( "America/Caracas" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( zoneId ) ;

I cannot provide more specific code examples because your question is confused. Your exact goal is not clear.

Not zoned

If stored in a column of a type akin to the SQL-standard TIMESTAMP WITHOUT TIME ZONE, retrieve as a LocalDateTime.

LocalDateTime ldt = myResultSet.getObject( … , LocalDateTime.class ) ;

This type cannot represent a moment, a point on the timeline. Lacking the context of a time zone or offset-from-UTC means we do not know if, for example, noon on a particular date is noon in Tokyo Japan, noon in Toulouse France, or noon in Toledo Ohio US — all different moments, several hours apart. Therefore, this type cannot be adjusted into an arbitrary time zone.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

Where to obtain the java.time classes?

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
1

Get date from database using JDBC 4.2 (no SimpleDateFormat)

I recommend that you use java.time, the modern Java date and time API, for your date and time work. Since JDBC 4.2 we can retrieve java.time types like LocalDate directly from SQL. If your data type in the database is date (recommended for a date without time of day):

    PreparedStatement selectStatement = yourDatabaseConnection.prepareStatement("select your_date from your_table where id = 4;");
    ResultSet rs = selectStatement.executeQuery();
    if (rs.next()) {
        LocalDate date = rs.getObject("your_date", LocalDate.class);
    }

If the datatype in SQL is varchar or char (not recommended), retrieve into a String in Java and parse it. The following example assumes ISO 8601 format, like 2021-02-25, but can be tailored to other formats using a DateTimeFormatter.

        LocalDate date = LocalDate.parse(rs.getString("your_date"));

Add time and time zone

By adding a time and a time zone I am guessing that you intended the following:

        LocalTime timeOfDay = LocalTime.of(16, 0); // 4 PM
        ZoneId zone = ZoneId.of("America/Caracas");
        
        ZonedDateTime zdt = date.atTime(timeOfDay).atZone(zone);
        
        System.out.println(zdt);

If date was 2021-02-25, the output is:

2021-02-25T16:00-04:00[America/Caracas]

Despite its name the outdated java.util.Date class represented nothing but a point in time, that is, without time zone. The modern ZonedDateTime on the other hand is a date and time of day in a time zone, just as the name hints. So what you need when you want a date with time and time zone.

I’d like to explain the central expression, date.atTime(timeOfDay).atZone(zone) a bit more:

        date                    // A LocalDate, that is, a date without time of day
             .atTime(timeOfDay) // A LocalDateTime, with specified time of day, still without time zone
             .atZone(zone)      // A ZonedDateTime. The same date and time, now in the specified time zone.

Link

Oracle tutorial: Date Time explaining how to use java.time.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • 1
    *"The modern ZonedDateTime on the other hand is a date and time of day in a time zone"* ... All true. Just adding that time zone rules can be arbitrarily complex and a source of potential errors, esp. when trying to accurately use historical timestamps (eg. state of Indiana). The API documentation for `java.time` recommends against using `ZonedDateTime` at the business logic and persistence levels and only using it at the UI level. At the other levels, to avoid unnecessary complexity, `Instant` or `OffsetDateTime` are recommended. – scottb Feb 25 '21 at 19:37
  • Thanks, @scottb, that’s true and a good recommendation. I didn’t see it in the API doc, so if you have got chapter and verse, or a link, I’d be interested? – Ole V.V. Feb 25 '21 at 19:40
  • A somewhat accurate summary of comments in the package summary at *https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/time/package-summary.html* – scottb Feb 25 '21 at 20:18
  • @scottb I would disagree with your recommendations. While time zone rules were poorly recorded historically, the `tz data` file is generally accurate from around 1970 to present day, the time frame seen here in this Question and this Answer. If the intended zone is known, then you should definitely be using `ZonedDateTime` rather than `OffsetDateTime`. As the page you linked says: “… and OffsetDateTime, are intended primarily for use with network protocols and database access”. You cannot project forward or backward accurately using `OffsetDateTime` if your business logic requires a zone. – Basil Bourque Feb 25 '21 at 21:47
  • @BasilBourque: No argument, and I'll admit that I misrepresented the package documentation for `OffsetDateTime`. However, the general advice to use simpler datetimes at the persistence and business logic levels, and to convert for time zones at the UI level does seem to ring loudly true. – scottb Mar 01 '21 at 20:51