I am having an issue with converting a double to a Java date object in the format: yyyy-MM-dd HH:mm:ss
I have tried to convert this double value to a long value then instanciate a Date object with the result, but I got errors saying that I cannot convert from double to long.
my timestamp double values are in this format:
1.511554592277516E9
Can anybody help me with this issue please, thanks in advance.
Assuming that this floating point value is seconds past the Unix Epoch of 1 Jan 1970 0:00 GMT, this will provide a conversion to a LocalDateTime with that offset:
LocalDateTime localDateTime = LocalDateTime.ofEpochSecond(
Double.valueOf(1.511554592277516E9).longValue(), 0, ZoneOffset.UTC);
System.out.println(localDateTime);
I leave converting this to a Date as an exercise for the reader.
Your floating-point value, 1.511554592277516E9, no doubt denotes seconds since the epoch of January 1, 1970 at midnight UTC, with microsecond precision: 1 511 554 592 seconds and 277 516 microseconds (millionths of a second).
I suggest using java.time, the modern Java date and time API also known as JSR-310, for this. It is much nicer to work with than the outdated Date class and friends, and also offers nanosecond precision (Date only has millisecond precision, so you would lose precision if converting into one). More specifically I will first create a java.time.Instant object (conversion to other date-time types will be easy, I’ll touch on one example at the end).
Getting the full precision through into an Instant requires a little thought. I played a little with double and long, but realized (1) double doesn’t have the full precision required, the nanoseconds will not be right (2) converting to a long holding the nanoseconds (not the only way, but certainly the easiest) will create a “year 2262 problem”, so if you are handling dates in a far future, it will not work. In any case, I think that the easy and safe solution is to use BigDecimal for the math required before feeding the numbers into an Instant.
String secondsSinceEpoch = "1.511554592277516E9";
BigDecimal decimalSeconds = new BigDecimal(secondsSinceEpoch);
long seconds = decimalSeconds.longValue();
long nanos = decimalSeconds.subtract(BigDecimal.valueOf(seconds))
.movePointRight(9)
.longValueExact();
Instant inst = Instant.ofEpochSecond(seconds, nanos);
System.out.println(inst);
This prints:
2017-11-24T20:16:32.277516Z
The printed date-time is in UTC. If the value is as expected, I should say it confirms that your floating-point value was indeed seconds since the epoch.
You requested a date-time in the format yyyy-MM-dd HH:mm:ss. You will need to decide in which time zone you want the date-time. Date objects don’t have a format, so you will also need to get the format in a string. For example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(inst.atZone(ZoneId.of("America/Vancouver")).format(formatter));
This prints
2017-11-24 12:16:32
You substitute your desired time zone, of course. atZone() converts the Instant into a ZonedDateTime, another often used class of java.time.
EDIT: In case you don’t want to bother with BigDecimal, you accept a slight inaccuracy and/or your receive your value as a double rather than a string, here’s an alternative in fewer lines of code:
double secondsSinceEpoch = 1.511554592277516E9;
long longSeconds = (long) secondsSinceEpoch;
long micros = Math.round((secondsSinceEpoch - longSeconds) * 1_000_000);
Instant inst = Instant.ofEpochSecond(longSeconds).plus(micros , ChronoUnit.MICROS);
In this particular case it gives exactly the same result, down to the nanosecond. I’m not sure whether with other inputs, the microseconds may end up inaccurate, but on the other hand, if you receive a double (not a string), there’s nothing you could do about that anyway.
Related
I have a time in milliseconds: 1618274313.
When I convert it to time using this website: https://www.epochconverter.com/, I am getting 6:08:33 AM.
But when I use SimpleDateFormat, I am getting something different:
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
System.out.println(sdf.format(new Date(1618274313)));
I am getting output as 23:01:14.
What is the issue in my code?
In your example, you are using time 1618274313 and you are assuming that it is in milliseconds. However, when I entered the same time on https://www.epochconverter.com/, I got below results:
Please notice the site mentions: Assuming that this timestamp is in seconds.
Now if we use that number multiplied by 1000 (1618274313000) as the input so that the site considers it in milliseconds, we get below results:
Please notice the site now mentions: Assuming that this timestamp is in milliseconds.
Now, when you will use 1618274313000 (correct time in milliseconds) in Java with SimpleDateFormat, you should get your expected result (instead of 23:01:14):
SimpleDateFormat sdf=new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
System.out.println(sdf.format(new Date(1618274313000)));
use Instant.ofEpochSecond
long test_timestamp = 1618274313L;
LocalDateTime triggerTime =
LocalDateTime.ofInstant(Instant.ofEpochSecond(test_timestamp),
TimeZone.getDefault().toZoneId());
System.out.println(triggerTime);
it prints output as 2021-04-13T06:08:33
Assuming it is in milliseconds as you say, all you know for certain is that you have a specific duration.
Duration d = Duration.ofMillis(1618274313);
System.out.println(d);
Prints
PT449H31M14.313S
Which says it is 449 hours, 31 minutes and 14.313 seconds of duration. Without knowing the epoch of this duration and any applicable zone offsets, it is not really possible to ascertain the specific date/time it represents. I could make lots of assumptions and provide results based on that, but more information from you would be helpful.
java.time
As Viral Lalakia already spotted, the epoch converter that you linked to, explicitly said that it assumed that the number was seconds (not milliseconds) since the epoch. The following makes the same assumption in Java. I recommend that you use java.time, the modern Java date and time API.
ZoneId zone = ZoneId.of("Asia/Kolkata");
long unixTimestamp = 1_618_274_313;
Instant when = Instant.ofEpochSecond(unixTimestamp);
ZonedDateTime dateTime = when.atZone(zone);
System.out.println(dateTime);
System.out.println(dateTime.format(DateTimeFormatter.ISO_LOCAL_TIME));
Output is:
2021-04-13T06:08:33+05:30[Asia/Kolkata]
06:08:33
This agrees with the 6:08:33 AM that you got from the converter. And the date is today’s date. A coincidence?
If the number is indeed milliseconds (which I honestly doubt), just use Instant.ofEpochMill() instead of Instant.ofEpochSecond().
Instant when = Instant.ofEpochMilli(unixTimestamp);
1970-01-19T23:01:14.313+05:30[Asia/Kolkata]
23:01:14.313
This in turn agrees with the result you got in Java (except that the milliseconds are also printed).
What is the fastest way to write LocalDateTime instance to file and then read it from file and convert it back to LocalDateTime object?
I used to save milliseconds and then convert it to Date object. It looked pretty fast but now I am dealing with Java 8's LocalDateTime and it's not clear what would be most efficient way to save and retrieve it from file.
I don't think using DateTimeFormater is a good idea as it requires more resources to convert it to String and then parse the String.
Time zone is not relevant.
If you want to save the milliseconds and timezone is not important, you can use the java.time.Instant class - with only the LocalDateTime there's no way to get the milliseconds, because this class has no timezone/offset information.
// get the current date
Instant instant = Instant.now();
// get milliseconds (equivalent to java.util.Date.getTime())
long millis = instant.toEpochMilli();
// get Instant from milliseconds
Instant instant = Instant.ofEpochMilli(millis);
If you have a LocalDateTime, though, you can easily convert it to an Instant:
LocalDateTime d = LocalDateTime.now();
Instant instant = d.atOffset(ZoneOffset.UTC).toInstant();
This code obviously assumes that the values in the LocalDateTime correspond to an UTC date and time. To convert the Instant back to a LocalDateTime:
LocalDateTime d = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
PS: have you measured your system's performance to know if "fast" is really an issue? Anyway, I'm doing things the "standard" way (the most straightforward way based on what the API offers), is that what you want?
Maybe you can think that creating an Instant as an "intermediary" object makes things less "fast" (but you'll need to measure that anyway). If that's the case, you can get the millis from the LocalDateTime directly (assuming that it corresponds to a date and time in UTC):
// get the current date
LocalDateTime d = LocalDateTime.now();
// get milliseconds value
long millis = d.toEpochSecond(ZoneOffset.UTC) * 1000 + d.get(ChronoField.MILLI_OF_SECOND);
// get LocalDateTime from millis
LocalDateTime d = LocalDateTime.ofEpochSecond(millis / 1000, (int) (millis % 1000) * 1000000, ZoneOffset.UTC);
It's important to note that java.time classes have nanoseconds precision, so getting the milliseconds makes you lose this precision.
If you don't want to lose the nanoseconds precision and don't necessarily need to work with the millis value, you can store 2 different numbers (epoch day and nano of day):
// get the current date
LocalDateTime d = LocalDateTime.now();
// get values from LocalDateTime
long epochDay = d.toLocalDate().toEpochDay();
long nanoOfDay = d.toLocalTime().toNanoOfDay();
// save both values to file
// retrieve the LocalDateTime from the values
LocalDateTime d = LocalDateTime.of(LocalDate.ofEpochDay(epochDay), LocalTime.ofNanoOfDay(nanoOfDay));
This doesn't require the conversion to UTC, but it requires 2 numbers instead of one. You might think that creating a LocalDate and a LocalTime makes things slower, but these 2 objects are always created internally by LocalDateTime (in all cases).
Not sure, though, if all this math is "faster" enough than using an Instant. It's a matter of testing to see which one is best for your case.
But for me, the most "efficient" in terms of clarity and code-easy-to-maintain is using an Instant (or using the last approach of epoch day and nano of day). And unless you're dealing with millions of records, I'm not sure if that'll really be a performance issue.
I've made a simple test (run each case above 10 million times), and the last approach (using epoch day and nano of day) seems to be the fastest. But the difference is less than 1 second. Only by running 2 billion times I've got a 20-seconds difference, so if you're dealing with this many records, maybe it's worth it.
Regarding other resources (memory usage, CPU, I/O), I didn't check. But anyway, performance issues are very specific to each environment: depending on how the system is designed, how the system's parts/modules/components interact with each other, and many other factors, you can have different bottlenecks in each situation.
In the end, you'll have to test each approach and see which one performs best in your system. Or you can conclude that it doesn't make a significant difference (and for cases with less than a couple of millions records, maybe it doesn't - but you'll only know after benchmarking it).
I would appreciate any help with finding bug for this exception:
java.text.ParseException: Unparseable date: "2007-09-25T15:40:51.0000000Z"
and following code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date = sdf.parse(timeValue);
long mills = date.getTime();
this.point.time = String.valueOf(mills);
It throws expcetion with Date date = sdf.parse(timeValue); .
timeValue = "2007-09-25T15:40:51.0000000Z"; , as in exception.
Thanks.
Z represents the timezone character. It needs to be quoted:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
(Answer now extensively revised, thanks for the corrections in the comments)
In Java 7 you can use the X pattern to match an ISO8601 timezone, which includes the special Z (UTC) value.
The X pattern also supports explicit timezones, e.g. +01:00
This approach respects the timezone indicator correctly, and avoids the problem of treating it merely as a string, and thus incorrectly parsing the timestamp in the local timezone rather than UTC or whatever.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
Date date = sdf.parse("2007-09-25T15:40:51Z");
Date date2 = sdf.parse("2007-09-25T15:40:51+01:00");
This can also be used with milliseconds:
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
Date date3 = sdf2.parse("2007-09-25T15:40:51.500Z");
However, as others have pointed out, your format has 7-digit fractional seconds, which are presumably tenth-microseconds. If so, SimpleDateFormat cannot handle this, and you will get incorrect results, because each 0.1 microsecond will be interpreted as a millisecond, giving a potential overall error of up to 10,000 seconds (several hours).
In the extreme case, if the fractional second value is 0.9999999 seconds, that will be incorrectly interpreted as 9999999 milliseconds, which is about 167 minutes, or 2.8 hours.
// Right answer, error masked for zero fractional seconds
Date date6 = sdf2.parse("2007-09-25T15:40:51.0000000Z");
// Tue Sep 25 15:40:51 GMT 2007
// Error - wrong hour
// Should just half a second different to the previous example
Date date5 = sdf2.parse("2007-09-25T15:40:51.5000000Z");
// Tue Sep 25 17:04:11 GMT 2007
This error is hidden when the fractional seconds are zero, as in your example, but will manifest whenever they are nonzero.
This error can be detected in many cases, and its impact reduced, by turning off "lenient" parsing which by default will accept a fractional part of more than one second and carry it over to the seconds/minutes/hours parts:
sdf2.setLenient(false);
sdf2.parse("2007-09-25T15:40:51.5000000Z");
// java.text.ParseException: Unparseable date: "2007-09-25T15:40:51.5000000Z"
This will catch cases where the millis value is more than 999, but does not check the number of digits, so it is only a partial and indirect safeguard against millis/microseconds mismatches. However, in many real-world datasets this will catch a large number of errors and thus indicate the root problem, even if some values slip through.
I recommend that lenient parsing is always disabled unless you have a specific need for it, as it catches a lot of errors that would otherwise be silently hidden and propagated into downstream data.
If your fractional seconds are always zero, then you could use one of the solutions here, but with the risk that they will NOT work if the code is later used on non-zero fractional seconds. You may wish to document this and/or assert that the value is zero, to avoid later bugs.
Otherwise, you probably need to convert your fractional seconds into milliseconds, so that SimpleDateFormat can interpret them correctly. Or use one of the newer datetime APIs.
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work. Your string is in ISO 8601 format and can be directly parsed by the java.time.Instant class without us specifying any formatter:
String timeValue = "2007-09-25T15:40:51.0000000Z";
Instant i = Instant.parse(timeValue);
long mills = i.toEpochMilli();
String time = String.valueOf(mills);
System.out.println(time);
Output:
1190734851000
May use a formatter for output if desired
If we know for a fact that the millisecond value will never be negative, java.time can format it into a string for us. This saves the explicit conversion to milliseconds first.
private static final DateTimeFormatter EPOCH_MILLI_FORMATTER
= new DateTimeFormatterBuilder().appendValue(ChronoField.INSTANT_SECONDS)
.appendValue(ChronoField.MILLI_OF_SECOND, 3)
.toFormatter(Locale.ROOT);
Now formatting is trivial:
assert ! i.isBefore(Instant.EPOCH) : i;
String time = EPOCH_MILLI_FORMATTER.format(i);
And output is still the same:
1190734851000
In particular if you need to format Instant objects to strings in more places in your program, I recommend the latter approach.
What went wrong in your code?
First of all, there is no way that SimpleDateFormat can parse 7 decimals of fraction of second correctly. As long as the fraction is zero, the result will happen to come out correct anyway, but imagine a time that is just one tenth of a second after the full second, for example, 2007-09-25T15:40:51.1000000Z. In this case SimpleDateFormat would parse the fraction into a million milliseconds, and your result would be more than a quarter of an hour off. For greater fractions the error could be several hours.
Second as others have said format pattern letter Z does not match the offset of Z meaning UTC or offset zero from UTC. This caused the exception that you observed. Putting Z in quotes as suggested in the accepted answer is wrong too since it will cause you to miss this crucial information from the string, again leading to an error of several hours (in most time zones).
Link
Oracle tutorial: Date Time explaining how to use java.time.
I'm storing messages from an amazon cloud and ordering them by their timestamp in a sorted map.
I am parsing the timestamp from the cloud with the following code:
Date timestamp = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'", Locale.ENGLISH).parse(time);
and then I am storing in them in a sorted map with the key being the date.
The issue is that the date only comes down to seconds precision.
I can have several messages sent in 1 second, so I need them to be ordered with millisecond precision. Is there a data structure that allows this?
Well as long as your source has a higher resolution than 1 second. Looks like that from the pattern, but you haven't shown us any input example.
Date is just a wrapper around a long milliseconds since 1970-01-01. So you have that already. Date.getTime() will return that, with millisecond precision.
Why would you think that Date only has one second precision? Date.compareTo(Date anotherDate) compares on a millisecond level.
So your SortedMap should work fine unless you are doing something strange.
I am not sure if you have done this, but you can create your own comparator and use that.
As a side note, depending on your applications setup you may want to be careful with how you use SimpleDateFormat, there are some issues with it.
java.time
I am providing the modern answer: use java.time, the modern Java date and time API, for your date and time work. First of all because it is so much nicer to work with than the old date and time classes like Date and (oh, horrors) SimpleDateFormat, which are poorly designed. We’re fortunate that they are long outdated. An added advantage is: Your date-time string is in ISO 8601 format, and the classes of java.time parse this format as their default, that is, without any explicit formatter.
String stringFromCloud = "2014-06-14T08:55:56.789Z";
Instant timestamp = Instant.parse(stringFromCloud);
System.out.println("Parsed timestamp: " + timestamp);
Output:
Parsed timestamp: 2014-06-14T08:55:56.789Z
Now it’s clear to see that the string has been parsed with full millisecond precision (Instant can parse with nanosecond precision, up to 9 decimals on the seconds). Instant objects will work fine as keys for your SortedMap.
Corner case: if the fraction of seconds i 0, it is not printed.
String stringFromCloud = "2014-06-14T08:56:59.000Z";
Parsed timestamp: 2014-06-14T08:56:59Z
You will need to trust that when no fraction is printed, it is because it is 0. The Instant will still work nicely for your purpose, being sorted before instants with fraction .001, .002, etc.
What went wrong in your parsing?
First, you’ve got a problem that is much worse than missing milliseconds: You are parsing into the wrong time zone. The trailing Z in your incoming string is a UTC offset of 0 and needs to be parsed as such. What happened in your code was that SimpleDateFormat used the time zone setting of your JVM instead of UTC, giving rise to an error of up to 14 hours. In most cases your sorting would still be correct. Around transition from summer time (DST) in your local time zone the time would be ambiguous and parsing may therefore be incorrect leading to wrong sort order.
As the Mattias Isegran Bergander says in his answer, parsing of milliseconds should work in your code. The reason why you didn’t think so is probably because just a minor one of the many design problems with the old Date class: even though internally it has millisecond precision, its toString method only prints seconds, it leaves out the milliseconds.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
I have a certain value of date and time say 28-3-2012(date) - 10:36:45(time) . I wish to convert this whole timestamp to nanoseconds with the precision of nanoseconds. As in the user would input the time and date as shown but internally i have to be accurate upto nanoseconds and convert the whole thing to nanoseconds to form a unique key assigned to a specific object created at that particular time.
Could some one please help me with the same..
The precision of the sample date is seconds, the representation in long format is a value in millisecond units. You can convert that to nanosecond units by multiplying it with 1,000,000.
But, in fact, it will simply append nine zeros to the oririginal date value and not add any extra information that you'd need to create hashvalues. A Date object has no "hidden" nanosecond precision.
Java offers the method System.nanoTime() which returns a timevalue in nano second units. But we can't wrap it in a Date object without loosing precision. Nevertheless, it may help in your case to create unique "time values".
if you have Date object:
long nanosecond = date.getTime() * 1000000;
or you can parse your time string if its look like (dd/MM/yyyy HH:mm:ss)
DateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date = df.parse(dateString);
long nanosecond = date.getTime() * 1000000;
I suppose the simplest way would be to use JDK's support for this. Try following
TimeUnit.NANOSECONDS.toNanos(duration)
Here the single parameter duration will be the long value to which you want to convert to NanoSeconds.
Hope this helps.