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
Related
An old Stack Overflow posting suggests that the way to get the UTC timestamp in Java is the following:
Instant.now() // Capture the current moment in UTC.
Unfortunately this does not work for me. I have a very simple program (reproduced below) which demonstrates different behavior.
On Windows: the time is the local time and it is labeled with the offset with GMT
On Linux: the time is again the local time, and it is labeled correctly for the local timezone
Question: How do we display the UTC timestamp in a Java program?
My sample source code is as follows:
import java.time.Instant;
import java.util.Date;
public class UTCTimeDisplayer {
public static void main(String[] args) {
System.out.println(System.getProperty("os.name"));
Date currentUtcTime = Date.from(Instant.now());
System.out.println("Current UTC time is " + currentUtcTime);
}
}
Windows Output:
C:\tmp>java UTCTimeDisplayer
Windows 10
Current UTC time is Fri Jan 22 14:28:59 GMT-06:00 2021
Linux Output:
/tmp> java UTCTimeDisplayer
Linux
Current UTC time is Fri Jan 22 14:31:10 MST 2021
Your code:
Date.from(Instant.now())
You are mixing the terrible legacy classes with their replacement, the modern java.time classes.
Don’t.
Never use Date. Certainly no need to mix with java.time.Instant.
To explain your particular example, understand that among the Date class’ many poor design choices is the anti-feature of its Date#toString method implicitly applying the JVM’s current default time zone while generating its text.
You ran your code on two different JVMs that had different current default time zones. So you got different outputs.
Sun, Oracle, and the JCP gave up on the legacy date-time classes. So should we all. I recommend you not spend time trying understand Date, Calendar, SimpleDateFormat, and such.
You asked:
Question: How do we display the UTC timestamp in a Java program?
Instant.now().toString()
See that code run live at IdeOne.com.
2021-01-22T21:50:18.887335Z
You said:
On Windows: …
On Linux: …
You’ll get the same consistent results from Instant.now().toString() across Windows, Linux, BSD, macOS, iOS, Android, AIX, and so on.
Here is a table I made to guide you in transitioning from the legacy classes.
The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
I would suggest you simply use Instant.now() which you can convert to other java.time type.
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
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.
However, if you still want to use java.util.Date, use SimpleDateFormat as mentioned above.
Demo:
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
Date currentUtcTime = Date.from(Instant.now());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
System.out.println("Current UTC time is " + sdf.format(currentUtcTime));
}
}
Output:
Current UTC time is 2021-01-22 21:53:07 UTC
suggests that the way to get the UTC timestamp in Java is the following:
Instant.now() // Capture the current moment in UTC.
This, and most answers in this thread, are misleading.
Instant represents an instant in time. It's 'solarflares' time: Absolutely not one iota about it represents anything that is invented by human brains, and UTC is a timezone: A human invention. The cosmos, the sun, astronomy - they have no idea what UTC is, and don't care - and that's what Instant is all about. Instants are devoid of such human concepts as 'hours' or 'days' or 'timezones'. It makes no sense to ask an instant what day it happened on. It cannot tell you; some event occurred: If I ask a russian from the 19th century when that happened, they'll likely give a completely different answer vs. if I ask someone living a mere 100 miles west, for example. Instant doesn't know which localization to apply and thus doesn't let you ask it this question - that's a good thing, objects should not expose methods to which any answer it gives is either gobbledygook or at least requires knowing about all sorts of surprising caveats.
Crucially, if you tell me '... in UTC', you surely can tell with exacting detail which month, which day, etcetera. And Instant does not do this, which is why it is misleading to say that a java.time.Instant represents a moment of time in UTC. It doesn't. It represents a moment in time (not in any particular timezone).
Yeah, internally Instant, just like Date, is just a light wrapper around what System.currentTimeMillis() returns: "millis since epoch", but the crucial thing to understand about it, is that 'UTC' is not part of what it means, and therefore, when you give an Instant instance to some other method (such as System.out.println, to a database via JDBC, etc), that method is under absolutely no obligation to assume that UTC is semantically relevant.
When you want to mix human notions of time keeping (years, days, months, hours, minutes, milliseconds, and, yeah, timezones) with the notion of a more or less absolute* time, the right answer is java.time.ZonedDateTime. Note that any representation of time in something that isn't java.time.* based is by definition broken, as it is in most programming languages - turns out time is a lot more complex than most takes on a library to represent it realize. The fact that java.time is in effect the 4th attempt at writing a time library should be ample indication that it's hard to get it right.
ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
THAT is what you want - that isn't just implementation-detail-wise what you want, but it is code that exactly describes what you mean: Right now, at the UTC time zone, stored in an object that semantically doesn't just store the right time but also stores, and tightly entangles into its very identity, that it is specifically in UTC and is not to be re-interpreted, moved to the local zone, or any other such shenanigans - at least, not unless you explicitly ask it to do so.
Date currentUtcTime = Date.from(Instant.now());
Note that Date is the old API and therefore necessarily broken. In this case, Date is a lying liar who lies - it doesn't represent a date, it represents an instant; it is badly named. (The second API is Calendar, also broken. For example, that is also a lying liar who lies: It doesn't represent a Calendar whatsoever. It represents some bizarre amalgamation of a zoned datetime and an instant and is fit to represent neither as a consequence). Any time you go to the Date API weirdness ensues, and something as simple as 'I just want the concept of the time, at some specific moment, in UTC' isn't possible in these APIs. You are now dependent on barely defined behaviour of all the various libraries up and down the chain - you're effectively stuck praying that they do the right thing, or delving into exotic settings to try to cajole these libraries into doing what you want.
TL;DR: Use java.time.
*) Note that ZonedDateTime is not absolute. For example, if you have the time January 20th, 2023, 8 in the morning, at Europe/Amsterdam, in the form of a ZonedDateTime object, then the amount of seconds that will pass between now and that moment sure seems like it does not change and will not change when e.g. amsterdam goes through an hour change due daylight savings. However, if the dutch government decrees that henceforth The Netherlands will no longer move the clocks at all and will stay in summer time forever (which is likely - EU directive is already in place, it's now just a matter of when), then at the moment the gavel lands, your appointment shifts by 1 hour exactly.
That hopefully provides crucial insight in the difference: Instant, representing events (hence why I like to call it 'solarflares time', to disentangle it from human time keeping concepts as much as possible), doesn't even understand the very concept of such a decision having an effect on things. ZonedDateTime on the other hand is inherently bound up in it - hence the Zone in ZonedDateTime.
If you want to store barber appointments and use Instant to do it, you WILL be an hour late or early sooner rather than later.
An Instant object and also a Date object by themselves
only contain a point in time, but no timezone information.
Furthermore, the toString() method of the Date class
implicitly chooses the timezone provided by the system environment,
which is not what you want.
Therefore you need to chose the timezone (in your case UTC) explicitly.
For example like this:
Instant instant = Instant.now();
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);
System.out.println("Current UTC time is " + offsetDateTime);
This will (independently from the operation system) print
Current UTC time is 2021-01-22T22:37:21.950354100Z
where the trailing Z denotes the zero timezone offset (i.e. UTC).
A simple method that could work!
My requirement was date time with milliseconds
2021-11-25 19:55:00.743
private String getUTCTimestamp() {
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);
return utc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
Instant.now() is essentially the period of time since the epoch, (midnight 1 January 1970 in UTC), but you are using a Date to present that instant. Date is a reflection of the instant with millisecond precision, but as explained in the documentation at https://docs.oracle.com/javase/8/docs/api/java/util/Date.html, presenting a date should be done using a Calendar, as the presentation of a Date depends on the host. Essentially Date wraps the instant but is displayed according to other factors.
The simplest approach now if you want to output the instant is to use OffsetDateTime so that you can elect to present the instant in your desired timezone - UTC in your case. Use either OffsetDateTime.now() or OffsetDateTime.ofInstant() but if you are using the instant within your application logic then just stick with Instant.
Sometimes your program has to work with older java versions, so here is an example for 1.5:
java.text.SimpleDateFormat tfGMT = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Calendar cUTC = java.util.Calendar.getInstance (java.util.TimeZone.getTimeZone ("GMT+0"));
tfGMT.setCalendar (cUTC);
java.util.Date d= new java.util.Date ();
String s= tfGMT.format (d);
System.out.printf ("now=%s [unix ts=%d.%03d]\n", s, d.getTime()/1000, d.getTime()%1000);
Mind you, the first three lines don't have to be repeat at every call, but keep in mind that SimpleDateFormat is not thread-safe. (Simple solution: create one for each thread.)
Example usage (it shows that setting TZ doesn't affect UTC-timestamp):
$ TZ=GMT+3 java5 now_utc; TZ=GMT-3 java5 now_utc
now=2021-01-24 12:56:14 [unix ts=1611492974.264]
now=2021-01-24 12:56:14 [unix ts=1611492974.726]
Hi I read some article about new java.time package.And some of articles say we shouldnt use java.util.Date family.We can use offsetDateTime or LocalDateTime instead of java.util.Date.
And I am wondering about what should we use instead of Timestamp in new java.time package?
As I check Timestamp use
public class Timestamp extends java.util.Date
There are two types of time spans in Java 8:
Period - represents time difference between two points in years, months, days
Duration - represents a time duration - the actual physical time-span, doesn't depend on what is the start time
For example 1 month "period" will be 30 days in April, 31 in may, so the values might be calculated differently in different contexts. Also time zone changes (daylight saving) are taken into account. Duration of certain number of seconds, minutes, hours, days, will not depend on the context. For example 30 days will always be 30 days.
You have a good description in the doc:
https://docs.oracle.com/javase/tutorial/datetime/iso/period.html
Timestamp equivalent
I am assuming that you are referring to the outdated java.sql.Timestamp and/or to the timestamp (without time zone) and timestamp with time zone datatypes of SQL. The first was designed for use with the last two. The answer is different for the two.
For a timestamp in SQL timestamp with time zone is clearly recommended since it actually unambiguously defines a point in time, which is in the definition of what a timestamp is. For a timestamp with time zone you should use the java.time class that you already mentioned, OffsetDateTime. Some JDBC drivers and JPA implementations will accept Instant too.
In most SQL dialects a mere timestamp is a date and time of day with high resolution (for example microseconds) without time zone. Lacking time zone it does not define a point in time, so calling it a timestamp is really a lie. In any case the corresponding java.time type is the other class you mentioned, LocalDateTime.
All of the mentioned java.time classes have resolution of nanoseconds. I know of no SQL dialect that would demand more than that.
You are fully correct. Not only is java.util.Date poorly designed and long outdated. Timestamp is a true hack on top of that class. I recommend you don’t use any of them.
Timespan equivalent
Artur Olszak in another answer has already nicely given the basis of Period and Duration. There is no need for me to repeat that. As a supplement, please be aware that even though Duration has methods for converting to and from a number of days, it isn’t really well suited for days since it assumes that a day is always 24 hours, which is not always the case because of summer time (DST) and other anomalies. As soon as you need to count days, I recommend either Period or a simple number of days.
Links
Wikipedia article: Timestamp.
My answer to a related quetsion about retrieving an SQL timestamp into a java.time type.
Could somebody please help with getting UTC-converted Java timestamp of current local time?
The main goal is to get current date and time, convert into UTC Timestamp and then store in Ignite cache as a Timestamp yyyy-MM-dd hh:mm:ss[.nnnnnnnnn].
My attempt was Timestamp.from(Instant.now()). However, it still considers my local timezone +03:00. I am getting '2020-02-20 10:57:56' as a result instead of desirable '2020-02-20 07:57:56'.
How can I get UTC-converted Timestamp?
You can do it like this :
LocalDateTime localDateTime = Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(localDateTime.format(formatter));
Don’t use Timestamp
You most probably don’t need a Timestamp. Which is good because the Timestamp class is poorly designed, indeed a true hack on top of the already poorly designed Date class. Both classes are also long outdated. Instead nearly 6 years ago we got java.time, the modern Java date and time API. Since JDBC 4.2 this works with your JDBC driver too, and also with your modern JPA implementation.
Use OffsetDateTime
For a timestamp the recommended datatype in your database is timestamp with time zone. In this case in Java use an OffsetDateTime with an offset of zero (that is, UTC). For example:
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(now);
PreparedStatement statement = yourDatabaseConnection
.prepareStatement("insert into your_table (tswtz) values (?);");
statement.setObject(1, now);
int rowsInserted = statement.executeUpdate();
Example output from the System.out.println() just now:
2020-02-22T13:04:06.320Z
Or use LocalDateTime if your database timestamp is without time zone
From your question I get the impression that the datatype in your database is timestamp without time zone. It’s only the second best option, but you can pass a LocalDateTime to it.
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
The rest is the same as before. Example output:
2020-02-22T13:05:08.776
If you do need an old-fashioned java.sql.Timestamp
You asked for a Timestamp in UTC. A Timestamp is always in UTC. More precisely, it’s a point in time independent of time zone, so converting it into a different time zone does not make sense. Internally it’s implemented as a count of milliseconds and nanoseconds since the epoch. The epoch is defined as the first moment of 1970 in UTC.
The Timestamp class is a confusing class though. One thing that might have confused you is when you print it, thereby implicitly calling its toString method. The toString method uses the default time zone of the JVM for rendering the string, so prints the time in your local time zone. Confusing. If your datatype in SQL is timestamp without time zone, your JDBC driver most probably interprets the Timestamp in your time zone for the conversion into an SQL timestamp. Which in your case is incorrect since your database uses UTC (a recommended practice). I can think of three possible solutions:
Some database engines allow you to set a time zone on the session. I haven’t got any experience with it myself, it’s something I have read; but it may force the correct conversion from your Java Timestamp to your SQL timestamp in UTC to be performed.
You may make an incorrect conversion in Java to compensate for the opposite incorrect conversion being performed between Java and SQL. It’s a hack, not something that I would want to have in my code. I present it as a last resort.
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
Timestamp ts = Timestamp.valueOf(now);
System.out.println(ts);
2020-02-22 13:05:08.776
You notice that it only appears to agree with the UTC time above. It‘s the same result you get from the answer by Vipin Sharma except (1) my code is simpler and (2) you’re getting a higher precision, fraction of second is included.
Have you database generate the current timestamp in UTC instead of generating it in Java.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related question: Java - Convert java.time.Instant to java.sql.Timestamp without Zone offset
Despite what the Ignite docs say you can pass in a 24hr time.
The docs says yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] so you may be tempted in your code to use this to format your dates but this will lead to times after midday being wrong. Instead, format your dates with yyyy-MM-dd HH:mm:ss[.nnnnnnnnn].
Notice the upper case HH. If you're using ZonedDateTime or Joda's DateTime when you call now with UTC now(UTC) and then toString("yyyy-MM-dd HH:mm:ss") will store the correct time in UTC.
Before Java-8 I got accustomed to always keep anything date/time related as milliseconds since Epoch and only ever deal with human readable dates/times on the way out, i.e. in a UI or a log file, or when parsing user generated input.
I think this is still safe with Java-8, and now I am looking for the most concise way to get a formatted date out of a milliseconds time stamp. I tried
df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))
but it bombs out with Unsupported field: YearOfEra in Instant.getLong(...) which I half understand. Now what to use instead of Instant?
LocalDateTime.ofEpoch(Instant, ZoneId) seems wrong, since I don't care to have local time. I just want to see the local time zone when applying the formatter. Internally it should be just the Instant.
The same goes for ZonedDateTime.ofInstant(Instant, ZoneId), I thought to apply the ZoneId only when formatting. But I notice that the DateTimeFormatter does not itself deal anymore with time zones, it seems, so I reckon I need to use one of the above.
Which one is preferred and why? Or should I use yet another way to format an epoch-millis time stamp as a date/time with time zone?
An Instant does not contain any information about the time-zone, and unlike in other places, the default time-zone is not automatically used. As such, the formatter cannot figure out what the year is, hence the error message.
Thus, to format the instant, you must add the time-zone. This can be directly added to the formatter using withZone(ZoneId) - there is no need to manually convert to ZonedDateTime *:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))
* regrettably, in early Java 8 versions, the DateTimeformatter.withZone(ZoneId) method did not work, however this has now been fixed, so if the code above doesn't work, upgrade to the latest Java 8 patch release.
Edit: Just to add that Instant is the right class to use when you want to store an instant in time without any other context.
The error you have when formatting an Instant using a formatter built with a year or other fields is expected; an Instant does not know which year or month or day it is, it only knows how much milliseconds have elapsed since the Epoch. For the same instant, it could be 2 different days on 2 different places of the Earth.
So you need to add a time zone information if you want to print the day. With an Instant, you can call atZone(zone) to combine it with a ZoneId in order to form a ZonedDateTime. This is very much like an instant, only that it has a time zone information. If you want to use the system time zone (the one of the running VM), you can get it with ZoneId.systemDefault().
To print it, you can use the two built-in formatter ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME. The difference between the two is that the zoned date time formatter will add the zone id to the output.
Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));
when run on my machine, which has a system time zone of "Europe/Paris", you'll get:
2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00
You can of course build your own formatter if those one do not suit you, using ofPattern or the builder DateTimeFormatterBuilder.
I agree that this is somewhat confusing, especially when compared with it's predecessor Joda DateTime.
The most confusing thing is that the documentation for LocalDateTime says that it is "A date-time without a time-zone", and yet LocalDateTime.ofInstant method takes both an instant and a timezone as parameters.
That said, I think that you can achieve what you want by using Instant and LocalDateTime.ofInstant by using the UTC timezone.
public LocalDateTime millisToDateTime(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
}
I have to prepare some app that will graph the use of resources over time, but there is one day on the year that has 25 hours (the day with 23 hours is not a big problem).
How can I represent that with a Date? What would be the best way of doing it?
I would like to use Date class, (as it works, is Comparable and so on) as a key, but I'm not sure if this would work... Any ideas?
The Date class itself simply represents an instant in time, from the UTC Unix epoch. It has no concept of time zones, calendars etc.
It's hard to know what exactly you're trying to represent, but in general Joda Time is a much better date/time API than the types in java.util.*.
My main advice on thinking about time-related issues is to be really, really clear about what concept each value is meant to be representing. If you're interested in a local date (a date within a particular calendar, with no reference to a particular time zone), then Joda Time's LocalDate class is probably what you're after. If you need to associate time zone information, then DateTime is probably your best bet - although that does represent an instant within a particular calendar and time zone, rather than a whole day.
It important not to confuse how time is represented and how it is displayed.
In its representation, you have only the number of milli-seconds since 1/1/1970. When you do calculations on this you are just comparing this long value.
When you display this time/date, depending on your timezone, you can have a period of 25 hours or 23 hours with the same day.