0

I am trying to read a String in the format 2019-12-23 13:35:00 to create a DateTime Object and associate it with a specific timezone to which I know it corresponds (GMT-5)

I am trying to do it this way:

 val string1 = "2019-12-23 13:35:00"
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    sdf.setTimeZone(StaticDateTimeZone.forID("Etc/GMT-5").toTimeZone)
    val date = sdf.parse(string1)

The output is not what I desire, as it is not assuming the datetime is not in the GMT-5 timezone, but in my system's timezone (GMT+0) , adjusting the datetime to GMT-5 as such: Mon Dec 23 08:35:00 GMT 2019

How can I create a datetime object that does not change the input, and just associates a timezone to it?

LeYAUable
  • 1,613
  • 2
  • 15
  • 30
  • 1
    I recommend you don’t use `SimpleDateFormat`, `TimeZone` and `Date`. Those classes are poorly designed and long outdated, the first in particular notoriously troublesome. Instead use `LocalDateTime`, `DateTimeFormatter` and other classes from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Dec 25 '19 at 07:41

3 Answers3

4

It seems you are using JodaTime and then convert to java.util.Date. You can instead use java.time package from Java 8 directly:

val string1 = "2019-12-23 13:35:00"
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val date = LocalDateTime.parse(string1, formatter)
date.atZone(ZoneId.of("Etc/GMT-5"))

which gives:

date: java.time.LocalDateTime = 2019-12-23T13:35
res0: java.time.ZonedDateTime = 2019-12-23T13:35+05:00[Etc/GMT-5]

LocalDateTime produces a date without any timezone attached to it. The last line effectively creates a new ZonedDateTime from the previous date and adds the given time zone on top of it. If you need to go back to java.util.Date, see this answer.

jrook
  • 3,459
  • 1
  • 16
  • 33
4

(Using Java syntax rather than Scala)

tl;dr

a specific timezone to which I know it corresponds (GMT-5)

GMT-5 is not a time zone, it is a mere offset. A time zone has a name in Continent/Region format, such as Africa/Tunis. A time zone is a history of changes in offset used by a region.

You cannot reliably determine a time zone from an offset. Many time zones may share the same offset on a particular moment, but at other moments may differ. So you would only be making a wild guess. Apply a time zone only if you know the particular zone with certainty.

Instead, use the offset itself to determine a moment, without a zone. Here is a one-line solution.

LocalDateTime                                     // Represent a date with time-of-day but lacking the context of an offset-from-UTC or a time zone.
.parse(                                           // When parsing, no need to specify a formatting pattern when the input complies with the ISO 8601 standard.        
    "2019-12-23 13:35:00".replace( " " , "T" )    // Comply with ISO 8601 standard formatting, replacing SPACE in the middle with an uppercase `T`. 
)                                                 // Returns a `LocalDateTime` object.
.atOffset(                                        // Determine a moment by placing the date and time-of-day of a `LocalDateTime` into the context of an offset-from-UTC.
    ZoneOffset.ofHours( -5 )                      // Represent an offset-from-UTC with `ZoneOffset`, merely a number of hours-minutes-seconds ahead or behind the prime meridian. 
)                                                 // Returns a `OffsetDateTime` object.
.toString()                                       // Generate text in standard ISO 8601 format.

Run code live.

2019-12-23T13:35-05:00

java.time

ISO 8601

Your input string is nearly in ISO 8601 standard format. To comply, replace the SPACE in the middle with a T.

String input = "2019-12-23 13:35:00".replace( " " , "T" ) ;

LocalDateTime

Parse as a LocalDateTime given your input's lack of a time zone or offset-from-UTC.

LocalDateTime ldt = LocalDateTime.parse( input ) ;

Be aware that a LocalDateTime is not a moment, is not a point on the timeline. In your example of approximately 1:30 PM, we do not know if this means 1:30 PM in Tokyo Japan or 1:30 PM in Toledo Ohio US, two very different moment many hours apart.

OffsetDateTime

You claim to know for certain that this date and time was meant to be seen through the lens of an offset five hours behind UTC (generally west of the prime meridian).

ZoneOffset offset = ZoneOffset.ofHours( -5 ) ;  // Five hours before UTC.

So let's apply that ZoneOffset to get an OffsetDateTime.

OffsetDateTime odt = ldt.atOffset( offset ) ;

See this code run live at IdeOne.com.

odt.toString(): 2019-12-23T13:35-05:00

ZonedDateTime

A time zone is always preferable to a mere offset-from-UTC. An offset is a number of hour--minutes-seconds, nothing more. A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region.

But do not guess at a time zone. Apply a zone only if you know your LocalDateTime object was originally intended for that zone.

Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).

ZoneId z = ZoneId.of( "America/Panama" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;

Table of date-time types in Java, both modern and legacy


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.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

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

For example, I have written a method:

    public static Date getDateWithTimeZone(String dateString, String format, String timeZone) {
        DateFormat df = new SimpleDateFormat(format);
        Date dateObj = null;

        try {
            dateObj = df.parse(dateString);
            DateTime dateTime = new DateTime(dateObj);
            TimeZone zone = TimeZone.getTimeZone(timeZone);
            return dateTime.withZoneRetainFields(DateTimeZone.forTimeZone(zone))
                    .toDate();
        } catch (ParseException e) {
            logger.error("Error while converting string to date",e);
        }
        return null;
    }
  • You can change the method/exception handling as you want. Hope this helps.
plr108
  • 1,201
  • 11
  • 16
Mehul Gayate
  • 344
  • 3
  • 7
  • 2
    FYI: The [*Joda-Time*](http://www.joda.org/joda-time/) project is now in [maintenance mode](https://en.wikipedia.org/wiki/Maintenance_mode). Its creator, [Stephen Colebourne](https://stackoverflow.com/users/38896/jodastephen), went on to lead [JSR 310](https://jcp.org/en/jsr/detail?id=310) defining the [*java.time*](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/package-summary.html) classes built into Java 8+. See [Tutorial by Oracle](https://docs.oracle.com/javase/tutorial/datetime/TOC.html). – Basil Bourque Dec 24 '19 at 21:50