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]
My requirement is to store all dates & date-times in UTC timezone in the database. I am using Java 8's LocalDate & LocalDateTime in my Hibernate entities.
Is that correct as LocalDate & LocalDateTime doesn't have timezone associated with them?
If not, should I fall back to using good old (or legacy?) Date & Timestamp?
Or should I be using Java 8's Instant? If using Instant, will there be a possibility to store only the date part, without time?
The database is MySQL & SQL Server and this is a Spring Boot application.
The “Local…” types purposely have no concept of time zone. So they do not represent a moment on the timeline. A LocalDateTime represents a vague range of possible moments but has no real meaning until assigning an offset or time zone. That means applying a ZoneId to get a ZonedDateTime.
For example, to say that Christmas this year starts at the first moment of December 25, we say:
LocalDateTime ldt = LocalDateTime.of( 2017 , 12 , 25 , 0 , 0 , 0 , 0 );
But that stroke of midnight happens earlier in the east than in the west.
That is why the elves’ Logistics department maps out Santa’s route starting at Kiribati in the Pacific, the earliest time zone in the world at 14 hours ahead of UTC. After delivering there, they route Santa westward to places like New Zealand for its midnight later. Then on to Asia for their midnight later. Then India, and so on, reaching Europe for their midnight several hours later, and then the east coast of North America for their midnight a few hours more later. All of these places experienced that same LocalDateTime at different moments, each delivery represented by a different ZonedDateTime object.
So…
If you want to record the concept of Christmas starting after midnight on the 25th, use a LocalDateTime and write into a database column of type TIMESTAMP WITHOUT TIME ZONE.
If you want to record the exact moment of each delivery Santa makes, use a ZonedDateTime and write into a database column of type TIMESTAMP WITH TIME ZONE.
About that second bullet, be aware that nearly every database system will use the zone information to adjust the date-time to UTC and store that UTC value. Some save the zone information as well, but some such as Postgres discard the zone info after using it to adjust into UTC. So “with time zone” is something of a misnomer, really meaning “with respect for time zone”. If you care about remembering that original zone, you may need to store its name in a separate column alongside.
Another reason to use Local… types is for future appointments. Politicians enjoy frequently changing their time zone(s) of their jurisdiction. They like to adopt Daylight Saving Time (DST). The like to change the dates of their DST cutovers. They like to drop their adoption of DST. They like to redefine their time zones, changing the boundaries. They like to redefine their offset-from-UTC sometimes by amounts like 15 minutes. And they rarely give advance notice, making such changes with as little as a month or two of warning.
So to make medical check-up appointment for next year or in six months, the time zone definition cannot be predicted. So if you want an appointment of 9 AM, you should use a LocalTime or LocalDateTime recorded in a database column of type TIMESTAMP WITHOUT TIME ZONE. Otherwise that 9 AM appointment, if zoned where the DST cutover is postponed, may appear as 8 AM or 10 AM.
When generating a projected schedule, you can apply a time zone (ZoneId) to those “local” (unzoned) values to create ZonedDateTime objects. But do not rely on those too far out in time when politicians may ruin their meaning by changing the zone(s).
Tip: These frequent changes to DST and time zones means you must keep your time zone tzdata data base up to date. There is a tzdata in your host OS, your JVM, and perhaps in your database system such as Postgres. All three should be frequently updated. Sometimes the zones change faster than the planned update cycles of those products such as Turkey last year deciding to stay on DST with only several weeks notice. So you may occasionally need to manually update those tzdata files. Oracle provides a tool for updating the tzdata of their Java implementations.
The general best practice in handling exact moments is to track them in UTC. Apply a time zone only where necessary such as in presentation to a user where they expect to see values in their own parochial time zone. In java.time, the Instant class represents a moment in the timeline. In UTC with a resolution of nanoseconds.
Instant instant = Instant.now() ; // Current moment on the timeline in UTC.
ZonedDateTime zdt = instant.atZone( z ) ; // Assign a time zone to view the same moment through the lens of a particular region’s wall-clock time.
Instant instant = zdt.toInstant(); // revert back to UTC, stripping away the time zone. But still the same moment in the timeline.
By the way, drivers that comply with JDBC 4.2 and later can deal directly with the java.time types via:
PreparedStatement::setObject
ResultSet::getObject
Oddly, the JDBC 4.2 spec does not require support for the two most common java.time types: Instant & ZonedDateTime. The spec does require support for OffsetDateTime. So you can easily convert back-and-forth.
Avoid the old legacy data types such as java.util.Date and java.sql.Timestamp whenever possible. They are poorly designed, confusing, and flawed.
Understand that all of these four are representations of a moment on the timeline in UTC:
Modern
java.time.Instant
java.time.OffsetDateTime with an assigned offset of ZoneOffset.UTC
Legacy
java.util.Date
java.sql.Timestamp
If you want a date-only value without time-of-day and without time zone, use java.time.LocalDate. This class supplants java.sql.Date.
As for specific databases, be aware that the SQL standard barely touches on the topic of date-time types and their handling. Furthermore, the various databases vary widely, and I really mean widely, in their support of date-time features. Some have virtually no support. Some mix SQL standard types with proprietary types that either predate the standard types or are intended as alternatives to the standard types. In addition, JDBC drivers differ in their behavior with marshaling date-time values to/from the database. Be sure to study the documentation and practice, practice, practice.
Or should I be using Java 8's Instant? If using Instant, will there be
a possibility to store only the date part, without time?
Instant should be suitable for the most operations.
Add hibernate-java8 to pom.xml to support Java 8 time API:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
<version>${version.hibernate}</version>
</dependency>
Then you can use LocalDate or LocalDateTime or Instant for Hibernate entity fields. You need to remove #Temporal(TemporalType.TIMESTAMP).
My requirement is to store all dates & date-times in UTC timezone in
the database. I am using Java 8's LocalDate & LocalDateTime in my
Hibernate entities.
Is that correct as LocalDate & LocalDateTime doesn't have timezone
associated with them?
You can set default JVM timezone somewhere in a configurational code:
#PostConstruct
void setUTCTimezone() {
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
Then you'll operate UTC time in your code.
To use Java 8 date types in DTO, you need to add Jsr310JpaConverters:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
And:
#EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
SpringBootApplication
public class Application { … }
More options:
Force Java timezone as GMT/UTC
How to set java timezone?
How to set a JVM TimeZone Properly
Part of the new Date API they split the types of dates up. The correct class that includes timezones is ZonedDateTime
// Get the current date and time
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("Zoned Date Time: " + zonedDateTime);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
Prints:
date1: 2007-12-03T10:15:30+05:00[Asia/Karachi]
Zoned Date Time: 2017-04-18T11:36:09.126-04:00[America/New_York]
ZoneId: Europe/Paris
CurrentZone: America/New_York
This question is related to this but this specific question focuses on the why. So no, this isn't a duplicate.
Quoting the answer:
The problem is that Java Date objects don't store a time zone. The
value is always in UTC, and is parsed and formatted in a given time
zone, usually the JVM's default time zone.
Oracle DATE columns are also stored without time zone, but should
represent the date as seen by the user. In 99.99% of cases, that means
the date in the JVM's default time zone.
So, the JDBC driver takes the Timestamp / Date value, which is in UTC,
converts it to the default time zone, and saves that to the database.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
What is it trying to solve by adjusting the value before saving it to the database?
The answers to these questions are the whys.
I couldn't see the benefit of the design and I can only see are the problems associated with it. Case in point is when saving is done in a specific timezone and retrieval is done in another timezone. The amount of questions being thrown at this specific topic just proves my point.
So ultimate question is, why it was designed that way? What are the reasons?
Date-time handling is a surprisingly complicated topic. Our intuitive understanding of time works against us as programmers, making this topic difficult to master. Furthermore, poor date-time handling in old databases and old classes make the job even more confusing.
java.time
Firstly, avoid the wretched old date-time classes bundled with the earliest versions of Java. Never use java.util.Date, java.util.Calendar, java.sql.Timestamp, nor other related classes. Use only the java.time classes. If you must interface with old code not yet updated to java.time, call on new conversions methods added to the old classes.
Date was replaced by Instant.
The problem is that Java Date objects don't store a time zone.
Not true. An Instant (and a Date) is always in UTC. Both the modern and legacy class represent a a count of fractional seconds since the first moment of 1970 in UTC, 1970-01-01T00:00:00Z. Always in UTC, easy-peasy.
Oracle DATE columns are also stored without time zone,
True.
The Oracle DATE data type represents only a date with time zone but lacks any concept of time zone or offset-from-UTC. This is apparently a legacy type, created before the SQL standard defined some basic date-time types. In the standard, the TIMESTAMP WITHOUT TIME ZONE might map close to Oracle DATE.
but should represent the date as seen by the user. In 99.99% of cases, that means the date in the JVM's default time zone.
I have no idea what the author meant by that. I think that is their clumsy way of saying that any type similar to the SQL-standard TIMESTAMP WITHOUT TIME ZONE simply takes any given date or date-with-time-of-day as-is, with no attempt at adjusting between zones or offsets. So if you pass January 21, 2018 at noon, it stores a value equivalent to this string 2018-01-23T12:00 without any regard to whether that was noon in Montréal Québec or noon in Kolkata India (two different moments, hours apart).
So, the JDBC driver takes the Timestamp / Date value, which is in UTC, converts it to the default time zone, and saves that to the database.
While the JDBC driver is unspecified here, I doubt this is its behavior. Such behavior would be a contradiction of the behavior of the Oracle DATE type which is no such zone adjustment. The Oracle DATE type (as I read the documentation; I'm not an Oracle user) is agnostic or unaware of zones/offsets.
In Java, the class mapping to SQL-standard TIMESTAMP WITHOUT TIME ZONE and Oracle DATE is LocalDateTime. You should use these zone-less types only in these three situations:
The zone or offset is unknown.This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
The intention is “everywhere”, as in, every time zone.For example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.Governments change the rules of their time zones with surprising frequency and with surprisingly little warning or even no warning at all. So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime. To print a report or display a calendar, dynamically apply a time zone (ZoneId) to generate a specific moment (ZonedDateTime or Instant). This must be done on-the-fly rather than storing the value.
What's exactly wrong with NOT adjusting and saving the value (UTC) as it is?
The JDBC driver should not be doing any adjustments to UTC for a type of Oracle Date or SQL-standard TIMESTAMP WITHOUT TIME ZONE.
If two users at 2018-06-06T21:53Z, one in Québec and one in India, both simultaneously save the current moment of their own parochial wall-clock time into a column of type SQL-standard TIMESTAMP WITHOUT TIME ZONE or Oracle DATE, then we should see two rows with values:
2018-06-06T17:53 (notice the date is “yesterday”)
2018-06-07T03:23 (notice the date is “tomorrow”)
The values are different because America/Montreal is four hours behind UTC while Asia/Kolkata is five and a half hours ahead of UTC, and no adjustment for time zones was made. To repeat myself yet again, the stored values here represent only a date and a time-of-day, but without any context of time zone or offset-from-UTC, they do not represent a moment.
The confusion may be coming from the fact that some databases such a Postgres do adjust incoming values into UTC for values heading to a column of a different type, the TIMESTAMP WITH TIME ZONE type (note the WITH versus WITHOUT). Postgres and something other databases use any passed zone/offset info to adjust into UTC value, then discard the zone/offset info. So the type name is something of a misnomer, and you can think of it as TIMESTAMP WITH RESPECT FOR TIME ZONE.
If those same two users seen above at 2018-06-06T21:53Z were saving the current moment into a SQL-standard column of type TIMESTAMP WITH TIME ZONE, then the two rows would appear as:
2018-06-06T21:53Z
2018-06-06T21:53Z
The Z at the end is pronounced Zulu and means UTC.
What is it trying to solve by adjusting the value before saving it to the database?
Generally, the best practice in date-time handling is to work in UTC rather than in other zones/offsets.
When working as a programmer or sysadmin, forget about your own parochial time zone. Translating back-and-forth from your own zone to UTC or other zones will drive you batty. Think of UTC as the One True Time; all other zones are but mere variations.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds (up to nine (9) digits of a decimal fraction).
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Store.
String sql = "INSERT INTO tbl ( event ) VALUES ( ? ) ;" ; // Writing a moment into a column of type `TIMESTAMP WTH TIME ZONE`.
myPreparedStatement.setObject( 1 , instant ) ; // As of JDBC 4.2 and later, we can directly exchange java.time objects with our database.
Retrieve.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
Present that moment in a particular time zone.
ZoneId z = ZoneId.of( "Asia/Kolkat" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
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 need to record the time of different systems whose default behavior would convert the input time into the systems' timezone. While what I want is to disable the convert. So in system 1, I need to construct a Calendar whose timezone is same with the system 2. For example, system 1's default timezone is PDT, system 2's default timezone is GMT, the time needed to create calendar is 2011/08/23 4:00 in PDT, what I need is to create a calendar in system 1 like 2011/08/23 4:00 in GMT.
In other words, How to create a Calendar without the concept of Timezone
I would abandon java.util.{Date, Calendar} at this point and flee to the comfort of Joda Time, where you would create a LocalDateTime. (Joda Time is a far superior date/time API.)
If you really want to stick with Calendar, you can just use the same time zone everywhere - the simplest approach being UTC as that doesn't have any daylight saving time.
Alternatively, it's not clear that you really want a Calendar without the concept of a TimeZone - but a Calendar which uses a TimeZone other than the system default - which is easy; you just set the time zone for the calendar explicitly. Of course you need to know the time zone of the other system that way...
If you can give more details of what information you know and what you need to do with it, we may be able to help you more.
Calendar from the definition is a date in some calendar system (typically Gregorian) and in a specified TimeZone.
If you don't care about time zone (or more precisely: you want points in time regardless of time zone), simply use Date. Despite its name, it actually stores the exact moment in time, not a date in some calendar.
How about just using the long epoch time returned from System.currentTimeMillis() and friends ?
java.util.Date does not have the concept of a timezone, it is just a thin wrapper around a GMT timestamp (if used correctly). It only may seem otherwise because its toString() method and some other legacy methods of the class implicitly use the system default timezone.
Calendar is only needed for date calculations, so you should not have to use it at all. All you need is to use SimpleDateFormat with the correct timezone to convert (format/parse) between Date instances (which do not have a timezone) and String representations (which have one, possibly GMT).
Have a look at Veyder-time. It is a simple and efficient framework for time and date manipulation, and has a natural representation of these objects. Veyder-time is completely free from all complications such as TimeZones, epochs, eras, different calendar systems and such.
With Veyder-time, creating time and date objects, without any time zone, is simple. Look at these examples:
Time now = Time.factory.now();
Time time = Time.factory.parse("2011-08-01 12:49:21.123");
There can be many use cases like
- we don't need time zone
- we don't need hours
- we don't need minutes
- we don't need milli seconds
In such cases we can just clear tall such fields which are causing trouble and are not required
calendar.clear(Calendar.ZONE_OFFSET);
calendar.clear(Calendar.MILLISECOND);
calendar.clear(Calendar.HOUR);
calendar.clear(Calendar.MINUTE);
All such fields are listed here - this link might be usful
https://docs.oracle.com/javase/7/docs/api/java/util/Calendar.html
java.time
The Answer by Jon Skeet is correct but now outdated.
As he said, the old legacy date-time classes are a mess and should be avoided. The Joda-Time project is now in maintenance mode, with the team advising migration to java.time.
LocalDateTime
The LocalDateTime class represents a date and a time-of-day with a resolution of nanoseconds. This value is unattached to the timeline, without any concept of offset-from-UTC nor time zone.
As such it does not represent an actual moment but a potential moment. When you place it in the context of an offset or time zone, then you imbue the new value (a OffsetDateTime or ZonedDateTime) with meaning as it becomes a point on the timeline.
For example, 2016-12-25T00:00:00 is when Christmas begins this year. But that stroke of midnight happens first in the Pacific such as Auckland New Zealand, which is earlier than Christmas in Kolkata India. And Christmas begins even later in Paris France, still later in Montréal Québec. That is why Santa starts in the east and works westward with his deliveries.
LocalDateTime xmas2016 = LocalDateTime.of( 2016 , Month.DECEMBER , 25 , 0 , 0 ) ;
Zoned
While your Question is not clear, I suspect what you really need is date-time values in UTC. All your various inputs in various time zones should be normalized into UTC.
Programmers should think of UTC as the One True Time. Most of your business logic, logging, data storage, and data exchange should generally be done in UTC. I suggest you keep a second clock on your desk set to UTC.
The Instant class represents a moment on the time line in UTC with a resolution of nanoseconds.
Instant instant = Instant.now();
When you need to see the wall-clock time for some offset-from-UTC, apply a ZoneOffset to get a OffsetDateTime object. To see the wall-clock time for some time zone, apply a ZoneId to get a ZonedDateTime.
ZonedDateTime xmas2016Montréal = xmas2016.atZone( ZoneId.of( "America/Montreal" ) );
Both of these offset/zoned date-time objects can get you back to an Instant with a call to toInstant.
Instant instant = xmas2016Montréal.toInstant(); // Convert to UTC.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP (see How to use…).
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.