8

I want the UNIX Epoch Time (Posix Time, Unix Time) of a string in some pattern, the string is in normal format (so UTC). Please using Java 8, not Joda or old Java.

(For milliseconds please see How to convert a date time string to long (UNIX Epoch Time) Milliseconds in Java 8 (Scala))

So far I have the below, but I hate this for a number of reasons:

  1. It's way too verbose for what is the most common thing to do with dates (convert to UNIX Epoch Time). 7 method calls for what should be 1.
  2. It has to specify UTC, but surely UTC is just a default, why do I have to be explicit here?
  3. It has a string literal "UTC"
  4. It has a magic number ZoneOffset.ofHours(0)

My best so far:

def dateTimeStringToEpoch(s: String, pattern: String): Long = 
    LocalDateTime.parse(s, DateTimeFormatter.ofPattern(pattern))
      .atZone(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0)))
      .toInstant().getEpochSeconds

Also, bonus question, is it efficient? Is there any overhead to creating the DateTimeFormatter via DateTimeFormatter.ofPattern(pattern)? If so why?

samthebest
  • 30,803
  • 25
  • 102
  • 142
  • 2
    At least there is a UTC constant [`ZoneOffset.UTC`](https://docs.oracle.com/javase/8/docs/api/java/time/ZoneOffset.html#UTC) – Flown Jan 24 '19 at 12:39
  • 3
    What input looks like? – xingbin Jan 24 '19 at 12:40
  • 3
    "It's way too verbose": this is why subroutines (aka functions or methods) have been invented very early in the history of computers. – Henry Jan 24 '19 at 12:58
  • 2
    Yes, there is probably an overhead from creating the `DateTimeFormatter` each time. If worried and the pattern is the same, make it a constant. You can safely do that, it is thread-safe. One method call less for each invocation… – Ole V.V. Jan 24 '19 at 13:13
  • I would have thought this question to be a duplicate (for example of [Convert a date format in epoch](https://stackoverflow.com/questions/6687433/convert-a-date-format-in-epoch)), but even though I would never use a count of method calls as a metric for code quality, there are some very interesting points coming up in the answers. – Ole V.V. Jan 24 '19 at 13:36
  • Most languages you can do it in 2 or less calls (one to parse to some struct, one to convert struct to unix time). In Unix C programming you can do it in 2, and C should really set the standard https://stackoverflow.com/a/1002631/1586965 – samthebest Jan 24 '19 at 17:23
  • It’s good that we don’t have to agree, @samthebest – Ole V.V. Jan 25 '19 at 10:12
  • @OleV.V. Count of native method calls is not THE metric, but it is useful. Suppose instead, it was 1000 method calls, you could, by that information alone, know that the code must be unnecessarily complex right? – samthebest Jan 26 '19 at 11:28
  • *"what is the most common thing to do with dates (convert to UNIX Epoch Time)."* Really? I've written lots of code with dates, and I've never had to convert a date to UNIX Epoch Time. By far the *most common thing* I've done with dates is formatting them for display. – Andreas Mar 12 '19 at 15:32

3 Answers3

3

This one is more than two times shorter (only 3 method calls):

def dateTimeStringToEpoch(s: String, pattern: String): Long = 
     LocalDateTime.parse(s, DateTimeFormatter.ofPattern(pattern))
                  .toEpochSecond(ZoneOffset.UTC)

Btw, I would build the DateTimeFormatter outside of dateTimeStringToEpoch and pass it as a method parameter:

def dateTimeStringToEpoch(s: String, formatter: DateTimeFormatter): Long = 
     LocalDateTime.parse(s, formatter).toEpochSecond(ZoneOffset.UTC)

Having actually run a performance test, there is little difference in performance (barely a factor of 2) in initialising the DateTimeFormatter outside the method.

scala> val pattern = "yyyy/MM/dd HH:mm:ss"
pattern: String = yyyy/MM/dd HH:mm:ss

scala>   time(() => randomDates.map(dateTimeStringToEpoch(_, pattern)))
Took: 1216

scala>   time(() => randomDates.map(dateTimeStringToEpochFixed))
Took: 732
samthebest
  • 30,803
  • 25
  • 102
  • 142
ETO
  • 6,970
  • 1
  • 20
  • 37
3

You may use the equivalent of the following Java code:

static long dateTimeStringToEpoch(String s, String pattern) {
    return DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC)
        .parse(s, p -> p.getLong(ChronoField.INSTANT_SECONDS));
}

Of course, processing DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC) implies work that could be avoided when encountering the same pattern string multiple times. Whether this amount of work is relevant for your application, depends on what is is doing beside this operation.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • 2
    Very nice! 4 method calls instead of 7 (and I disagree with the asker that it ought to be 1; there are a number of things we want to specify explicitly in the conversion). – Ole V.V. Jan 24 '19 at 13:16
  • 2
    @OleV.V. …and the result of `DateTimeFormatter.ofPattern(pattern).withZone(ZoneOffset.UTC)` could be reused, so the actual operation is the sole `parse` call, returning the desired value rather than something that needs to get converted. – Holger Jan 24 '19 at 13:18
2

Can you try this one, based on what you have said, it is parsing UTC time, so I have this as a sample.

Instant.parse("2019-01-24T12:48:14.530Z").getEpochSecond