0

I'm using SimpleDateFormat to convert string type of time to unix timestamp, but when I set timezone to an illegal value, I still got the correct result, is that how SimpleDateFormat.parse() method defined? If I wanna to have a unit test to test the failure case, how can I do that?

String str = "2016-06-21-10-19-22";

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-hh-mm-ss");
df.setTimeZone(TimeZone.getTimeZone("fewfefewf"));

Date date = df.parse(str);

long epoch = date.getTime();
System.out.println(epoch);
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
zzxdw
  • 65
  • 1
  • 6
  • 1
    I recommend you neither use `SimpleDateFormat`, `TimeZone` nor `Date`. Those classes are poorly designed and long outdated, the first in particular notoriously troublesome. Instead use `LocalDateTime`, `DateTimeFormatter` and `ZoneId`, all from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). The modern `ZoneId` will give you the validation that your asking for. – Ole V.V. Apr 24 '20 at 21:32

2 Answers2

1

It is because of getTimeZone method that returns default GMT zone if the specified TimeZone cannot be understood

the specified TimeZone, or the GMT zone if the given ID cannot be understood.

If would recommend to validate is by using TimeZone.getAvailableIDs, and easy check to validate input timezone is valid or not

Arrays.stream(TimeZone.getAvailableIDs()).anyMatch(tz->tz.equals("fewfefewf"))

And the final recommendation please use the java-8 date time api classes and start moving away from SimpleDateFormat

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"); // make sure you use H for hours in format

LocalDateTime dateTime = LocalDateTime.parse(str,formatter);
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • Hi @Deadpool, based on this, Is there any other method to convert string type of time to unix timestamp that will fail when I have illegal timezone? – zzxdw Apr 24 '20 at 18:58
  • Just validated it before converting as i showed @zzxdw – Ryuzaki L Apr 24 '20 at 19:03
  • Thanks for your suggestion, but seems I cant use DateTimeFormatter in my case, I tried time format like "2013-09-29T18:46:19Z", it will have an exception. – zzxdw Apr 24 '20 at 19:48
  • that's different format, just try googling with different formats https://stackoverflow.com/questions/61409243/error-while-parsing-2018-05-01t000000-date-using-instant-parse/61409497#61409497 – Ryuzaki L Apr 24 '20 at 19:50
  • thanks, I think I will still using SimpleDateFormat, SimpleDateFormat is able to parse all the formats. If using DateTimeFormatter, need to convert localDateTime to localDate in some cases..such as "2016-06-21-10-19-22" – zzxdw Apr 24 '20 at 20:20
  • Of course you not only can use but will also benefit from using `DateTimeFormatter` and the other classes from java.time. The exception you are talking about, is that the one I am addressing in [my answer](https://stackoverflow.com/a/61417933/5772882)? – Ole V.V. Apr 25 '20 at 05:50
0

java.time

I recommend that you use java.time, the modern Java date and time API, for your date and time work. Among its many advantages is that in many cases it gives you far better validation than the old classes like TimeZone, SimpleDateFormat and Date. Here’s my shot at the modern equivalent of your code:

    String str = "2016-06-21-10-19-22";

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd-hh-mm-ss");

    ZonedDateTime dateTime = LocalDateTime.parse(str, dtf)
            .atZone(ZoneId.of("fewfefewf"));

    long epoch = dateTime.toInstant().toEpochMilli();
    System.out.println(epoch);

Running it produces:

Exception in thread "main" java.time.format.DateTimeParseException: Text '2016-06-21-10-19-22' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
    at ovv.misc.MiscTest.<init>(MiscTest.java:72)
    at ovv.misc.MiscTest.main(MiscTest.java:61)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.LocalDateTime.from(LocalDateTime.java:461)
    at java.base/java.time.format.Parsed.query(Parsed.java:235)
    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
    ... 3 more
Caused by: java.time.DateTimeException: Unable to obtain LocalTime from TemporalAccessor: {MicroOfSecond=0, MinuteOfHour=19, NanoOfSecond=0, MilliOfSecond=0, SecondOfMinute=22, HourOfAmPm=10},ISO resolved to 2016-06-21 of type java.time.format.Parsed
    at java.base/java.time.LocalTime.from(LocalTime.java:431)
    at java.base/java.time.LocalDateTime.from(LocalDateTime.java:457)
    ... 5 more

Surprised? java.time has already found a bug that you didn’t ask about: in the format pattern string you need uppercase HH for hour of day from 00 through 23. Lowercase hh is for clock hour of AM or PM from 01 through 12. The detail to note in the error message is HourOfAmPm=10 — this is not what you intended. Let’s fix and run again:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
Exception in thread "main" java.time.zone.ZoneRulesException: Unknown time-zone ID: fewfefewf
    at java.base/java.time.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:279)
    at java.base/java.time.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:234)
    at java.base/java.time.ZoneRegion.ofId(ZoneRegion.java:120)
    at java.base/java.time.ZoneId.of(ZoneId.java:408)
    at java.base/java.time.ZoneId.of(ZoneId.java:356)
    at ovv.misc.MiscTest.<init>(MiscTest.java:73)
    at ovv.misc.MiscTest.main(MiscTest.java:61)

I believe that this is what you asked for. If we fix this error too, the code runs flawlessly:

    ZonedDateTime dateTime = LocalDateTime.parse(str, dtf)
            .atZone(ZoneId.of("America/Eirunepe"));

1466522362000

I put in a random time zone, but I think that you know which one you want.

The other answers have already explained nicely what went wrong in your code. I know of no way to persuade the old and poorly designed TimeZone class to validate the time zone ID string.

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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Hi Ole V.V., when I set date to "2016-06-21", and DateTimeFormatter.ofPattern("yyyy-MM-dd"), I got ```Text '2016-06-21' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {},ISO resolved to 2016-06-21 of type java.time.format.Parsed```, this error.. – zzxdw Apr 28 '20 at 18:58
  • Yes you do, @zzxdw. [Unable to obtain LocalDateTime from TemporalAccessor when parsing LocalDateTime (Java 8)](https://stackoverflow.com/questions/27454025/unable-to-obtain-localdatetime-from-temporalaccessor-when-parsing-localdatetime). Always good to paste your error message into your search engine. Your string contains a date and no time so cannot be parsed into a date *and* time, which is what a `LocalDateTime` is. Try for example `LocalDate.parse("2016-06-21").atTime(LocalTime.of(13, 37))` or `LocalDate.parse("2016-06-21").atStartOfDay()`. – Ole V.V. Apr 28 '20 at 19:04
  • what if I have these two formats(date with time, date with no time) at the same time, how to make those into one function? – zzxdw Apr 28 '20 at 19:15