0

I am trying to make timestamps from a csv of timestamp strings,

eg "21-Mar-21 05:01:26 GMT" and "19-Jul-21 1:08:22 BST"

SimpleDateFormat("dd-MMM-yy hh:mm:ss") gets the Date.

Is it possible to get the ZoneId from the "GMT" or "BST" strings? (BST being British Summer Time)

or do I need to hardcode a structure mapping one to the other?

icrovett
  • 435
  • 7
  • 21
P.Ellis
  • 55
  • 2
  • 10
  • Yo mean this? SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") add the Z to have the timezone on the date – icrovett Sep 17 '21 at 14:24
  • 2
    Note that these "not really time zone" abbreviations aren't unique. "CST" can mean various different things, for example. See https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations. If you can *possibly* avoid using these abbreviations, it's good to do so. – Jon Skeet Sep 17 '21 at 20:49
  • 1
    That’s delicate. While *GMT* is probably unambiguous, *BST* has a lot of possible meanings beside British Summer Time (Brazil(ian) Summer Time, Bangladesh Standard Time, Bougainville Standard Time). – Ole V.V. Sep 18 '21 at 15:33
  • Is there a more global solution? Your answers include hard-coding Locale.ENGLISH or ZoneId.of("Europe/London"), what if my application is running in Germany or Turkey and I only have the abbreviated timeZone in the string to go on? – P.Ellis Sep 20 '21 at 08:35
  • If you only have the abbreviation, give up, sorry. Those abbreviations are *very often* ambiguous, and your user will prefer a message saying you can’t interpret the abbreviation over a false result from interpreting the abbreviation wrongly. – Ole V.V. Sep 22 '21 at 10:25
  • Your string examples are in English. In Germany and Turkey they would probably use completely different date and time formats, so we’d need to know what those formats are. – Ole V.V. Sep 22 '21 at 10:34
  • 1
    Thank you, Ole v.v I thought that "give up" would be the answer, I just wanted to be sure. I shall make sure the CSV file is in UTC time + offset – P.Ellis Sep 22 '21 at 14:41

2 Answers2

3

java.time

The java.util Date-Time API and their formatting API, SimpleDateFormat are outdated and error-prone. Since java.sql.Timestamp extends java.util.Date, it inherits all undesirable things from its parent type. It is recommended to stop using them completely and switch to the modern Date-Time API*.

Solution using java.time, the modern Date-Time API:

import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "21-Mar-21 05:01:26 GMT";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd-MMM-uu HH:mm:ss VV", Locale.ENGLISH);
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime, dtf);
        System.out.println(zdt);

        // Getting ZoneId
        ZoneId zoneId = zdt.getZone();
        System.out.println(zoneId);

        // If required, get OffsetDateTime from the ZonedDateTime
        OffsetDateTime odt = zdt.toOffsetDateTime();
        System.out.println(odt);
    }
}

Output:

2021-03-21T05:01:26Z[GMT]
GMT
2021-03-21T05:01:26Z

ONLINE DEMO

Check this answer and this answer to learn how to use java.time API with JDBC.

If at all you need java.sql.Timestamp:

For any reason, if you need java.sql.Timestamp, simply get Instant out of the ZonedDateTime and derive the value of Timestamp using Timestamp#from.

Timestamp timestamp = Timestamp.from(zdt.toInstant());
System.out.println(timestamp);

If you just need java.sql.Timestamp, you can do it in the following alternative easier way:

import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "21-Mar-21 05:01:26 GMT";
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd-MMM-uu HH:mm:ss VV", Locale.ENGLISH);
        Instant instant = Instant.from(dtf.parse(strDateTime));
        Timestamp timestamp = Timestamp.from(instant);
        System.out.println(timestamp);
    }
}

Output:

2021-03-21 05:01:26.0

ONLINE DEMO

Learn more about the modern Date-Time API* from Trail: Date Time.

Update:

This update is based on the following valuable comment by Ole V.V.:

new DateTimeFormatterBuilder().appendPattern("dd-MMM-uu H:mm:ss ").appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Europe/London"))).toFormatter(Locale.ENGLISH) parses 19-Jul-21 1:08:22 BST into 2021-07-19T01:08:22+01:00[Europe/London], which agrees with what the OP wanted. The mentioned datetime string has 1 digit hour of day, 1, so we need just one H (which in turn also accepts the 05 from the other string example).

Demo:

import java.sql.Timestamp;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
import java.util.Locale;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "19-Jul-21 1:08:22 BST";
        
        DateTimeFormatter dtf = 
                new DateTimeFormatterBuilder()
                .appendPattern("dd-MMM-uu H:mm:ss ")
                .appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Europe/London")))
                .toFormatter(Locale.ENGLISH);
        
        Instant instant = Instant.from(dtf.parse(strDateTime));
        Timestamp timestamp = Timestamp.from(instant);
        System.out.println(timestamp);
    }
}

Output:

2021-03-21 05:01:26.0

ONLINE DEMO


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
1

If you know that GMT and BST are the only time zone abbreviations you will need, and you know for a fact that British Summer Time is the intended interpretation of BST, you can safely use the good answer by Arvind Kumar Avinash.

If there may be more time zone abbreviations in your input and you also know the correct interpretation for those, extending the answer is not difficult. Just pass a larger set of preferred time zones to DateTimeFormatterBuilder.appendZoneText(). For the sake of the example for the following formatter I have specified that BST is British Summer Time, PST is for Pitcairn Standard Time and CST means Cuba Standard Time.

    Set<ZoneId> preferredZones = Set.of(ZoneId.of("Europe/London"),
            ZoneId.of("Pacific/Pitcairn"), ZoneId.of("America/Havana"));
    DateTimeFormatter dtf = new DateTimeFormatterBuilder()
            .appendPattern("dd-MMM-uu H:mm:ss ")
            .appendZoneText(TextStyle.SHORT, preferredZones)
            .toFormatter(Locale.ENGLISH);

If you don’t know which time zone abbreviations may turn up in your data or you are not sure of the correct interpretation of each one of them, I think that’s it’s better to give up on the task. Sorry. Those abbreviations are very often ambiguous. Rather than a false result from interpreting the abbreviation wrongly your users will prefer a message stating that you cannot interpret the time zone abbreviation.

Link: Time Zone Abbreviations – Worldwide List.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161