I have been searching over the net from past few hours to get the datetime in my system timezone.
When I use calendar.getTimezone.getDefaultName()it always returns me GMT. Ideally it should return my current timezone, which is IST.
I am trying to convert this string "2014-02-14T06:04:00:00", which is in GMT to my timezone datetime. It always returns me the same time in GMT.
All I see is that everyone is suggesting to use Timezone, i.e,
dateFormatter.setTimezone("any_arbitary_timezone");
Point is my application will be used in different geographical locations. I cannot set it to a particular timezone. It should be set to the system timezone, so that it can display in whichever timezone the user is currently in.
tl;dr
Use the modern java.time classes.
ZonedDateTime.now( // Capture the current moment in the wall-clock time used by the people of a certain region (a time zone).
ZoneId.systemDefault() // Get the JVM’s current default time zone. Can change at any moment during runtime. If important, confirm with the user.
) // Renders a `ZonedDateTime` object. To see the same moment in UTC, extract a `Instant` object by calling `ZonedDateTime::getInstant`.
You may omit the explicit call to ZoneId.systemDefault. (But I do not recommend this.)
ZonedDateTime.now() // Capture the current moment in the JVM’s current default time zone.
Parse your string as a LocalDateTime, and adjust into desired time zone.
LocalDateTime.parse( "2014-02-14T06:04:00:00" ) // Parse a string lacking any indicator of time zone or offset-from-UTC. *Not* a specific point on the timeline.
.atOffset( ZoneOffset.UTC ) // Apply UTC as we are certain that offset-from-UTC of zero was intended by the supplier of that input string. Returns a `OffsetDateTime` object.
.atZoneSameInstant( // Adjust into another time zone. The `sameInstant` part means the same moment, different wall-clock time.
ZoneId.of( "Africa/Tunis" ) // Specify the particular zone of interest to you.
) // Returns a `ZonedDateTime` object.
Avoid java.util.Date & .Calendar
These legacy classes are notoriously troublesome. Sun/Oracle added the java.time package in Java 8 to supplant them. That package was inspired by Joda-Time.
Amongst the legacy classes’ problems is this confusing behavior: While a java.util.Date has no time zone information, it's toString implementation applies the JVM’s current default time zone when generating a String. So it misleads you by seeming to have a time zone when it does not.
java.time
I am trying to convert this string "2014-02-14T06:04:00:00", …
Your input string lacks any indicator of time zone or offset-from-UTC. So we parse as a LocalDateTime, which lacks any concept of zone/offset.
A LocalDateTime does not represent a moment, is not a point on the timeline. The word “Local” here does not mean a specific locality. It means “no specific locality at all”. Without the context of a zone/offset, it has no real meaning.
LocalDateTime ldt = LocalDateTime.parse( "2014-02-14T06:04:00:00" ) ;
… which is in GMT …
You say you are certain the supplier of that input string intended UTC as the context. We can apply an offset-from-UTC of zero, or UTC itself, to get an OffsetDateTime object. An OffsetDateTime is a moment, a point on the timeline. We can specify the ZoneOffset using the constant for UTC, ZoneOffset.UTC.
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
… to my timezone datetime
Apparently you want to adjust that moment into another time zone, to see the wall-clock time used by the people of a particular region. We need to apply a time zone (ZoneId) to get a ZonedDateTime.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;
Instead of specifying a time zone, you can ask your JVM for its current default time zone. Beware: The JVM’s current default time zone can be changed at any moment by any code in any thread of any app within that JVM.
ZoneId z = ZoneId.systemDefault() ;
ZonedDateTime zdt = odt.atZone( z ) ;
Point is my application will be used in different geographical locations.
Simply specify your desired/expected time zones explicitly. This is always good practice, in my opinion. The default time zone lies outside your control as a programmer, which makes it unreliable.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
Another tip: Work, think, store, and exchange in UTC. Forget about your own parochial time zone, as translating back-and-forth to your home zone will drive you batty. Think of UTC as the One True Time, and other zones/offsets are but mere variations.
Instant instant = Instant.now() ; // Capture current moment in UTC.
ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime zdtAuckland = instant.atZone( zAuckland ) ;
ZoneId zKolkata = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdtKolkata = instant.atZone( zKolkata ) ;
ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;
ZonedDateTime zdtCasablanca = instant.atZone( zCasablanca ) ;
There we have four ways ( instant, zdtAuckland, zdtKolkata, zdtCasablanca ) of looking at the very same simultaneous moment, the same point on the timeline.
instant.toString(): 2018-05-08T20:55:14.761721Z
zdtAuckland.toString(): 2018-05-09T08:55:14.761721+12:00[Pacific/Auckland]
zdtKolkata.toString(): 2018-05-09T02:25:14.761721+05:30[Asia/Kolkata]
zdtCasablanca.toString(): 2018-05-08T21:55:14.761721+01:00[Africa/Casablanca]
Zone vs Offset
An offset-from-UTC is simply a number of hours, minutes, and seconds. Nothing more, nothing less. Any number of time zones may share a particular offset at a particular moment.
A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. For example, Daylight Saving Time (DST) is a practice where the people of a region (inexplicably) decide to change their offset twice a year.
So a time zone is always preferable to a mere offset. Having a zone allows us to add or subtract time in a meaningful way, to account for changes in offset in that region’s history.
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 brought some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android (26+) bundle implementations of the java.time classes.
For earlier Android (<26), the process of API desugaring brings a subset of the java.time functionality not originally built into Android.
If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. 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.
Joda-Time
UPDATE: The Joda-Time project is now in maintenance mode. The team advises migration to the java.time classes. Skip to java.time section below in this Answer.
The Joda-Time package has good clear support for time zones. Unlike java.util.Date, a Joda-Time DateTime does know its own assigned time zone. If you fail to specify a time zone, the JVM's current default time zone is implicitly assigned.
DateTime dateTime = DateTime.now(); // Applies JVM’s default time zone implicitly.
I recommend against relying on the default time zone implicitly. Doing so leads to confusion and errors when doing date-time work.
DateTime dateTime = DateTime.now( DateTimeZone.getDefault() ); // Explicitly using default time zone.
If needed you may assign a time zone.
DateTime dateTimeKolkata = DateTime.now( DateTimeZone.forID( "Asia/Kolkata" ) ); // Specify a time zone.
For server-side work, best practice is to do business logic and database storage in UTC.
DateTime dateTimeUtc = DateTime.now( DateTimeZone.UTC ); // Assign UTC (GMT) time zone.
You can convert from the assigned time zone to another, including the JVM's current default time zone.
DateTime dateTime = dateTimeUtc.withZone( DateTimeZone.getDefault() );
Immutable
For thread-safety, Joda-Time uses immutable objects. Instead of modifying an object, methods such as withZone create a new instance based on the original.
Parse String
To parse a String as a DateTime, you must note whether the String includes an offset from UTC and/or a time zone. Yours does not. So you must specify a time zone by which to interpret that String. If you do not specify, the JVM’s current default time zone will be used during parsing.
In your Question, you said the String represents a date-time in UTC (GMT).
DateTime dateTimeUtc = new DateTime( "2014-02-14T06:04:00:00", DateTimeZone.UTC );
After parsing, you may assign another time zone if needed. Same moment in the time-line of the Universe, but shows a different Wall-Clock time.
DateTime dateTimeDefaultZone = dateTimeUtc.withZone( DateTimeZone.getDefault() );
So notice this was a two-step process. First we parsed your String using our external knowledge of that String's intended time zone because it lacked internal representation of that time zone or offset. Secondly we adjusted the time zone to another (the JVM default zone).
If your String had included an offset of +00:00 or the customary Z, we could have collapsed those two steps into one.
DateTime dateTimeDefaultZone = new DateTime( "2014-02-14T06:04:00:00Z", DateTimeZone.getDefault() ); // Apply time zone adjustment *after* parsing.
Note that this DateTime constructor looks like the one above but is actually quite different. This one's time zone argument is applied after parsing, rather than during parsing. Here the time zone argument is used to adjust the already-parsed DateTime. That Z on the end makes a world of difference.
Source of Default Time Zone
The JVM initially gets its default time zone from the host operating system. But be aware that a programmer can override this by:
Pass an argument on command-line when launching the JVM.
Call java.util.TimeZone.setDefault.
Doing this override affects all threads of all apps running in that JVM. So you should know that the JVM’s default time zone is usually the same as host OS but not necessarily the same.
Here is a way to get the id of a TimeZone that matches your local system clock's offset,
Calendar cal = Calendar.getInstance();
long milliDiff = cal.get(Calendar.ZONE_OFFSET);
// Got local offset, now loop through available timezone id(s).
String [] ids = TimeZone.getAvailableIDs();
String name = null;
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
if (tz.getRawOffset() == milliDiff) {
// Found a match.
name = id;
break;
}
}
System.out.println(name);
private String receivedFormat = "yyyy-MM-dd'T'HH:mm:ss", expectedFormat = "dd-MM-yyyy HH:mm:ss"; //Globall variables
//Write these three lines in your Test Class and below 2 methods
String dateString = "2018-08-14T07:00:00:00";
String returnString = correctDateFormat(dateString, receivedFormat, expectedFormat);
String result = getTimeInSelectedLocale(returnString);
Log.i("Ayaz", "Date: " +result);
/**
* #param receivedDate
* #param givenFormat
* #param expectedFormat
* #return returns date time in expected format
*/
public static String correctDateFormat(String receivedDate, String givenFormat, String expectedFormat) {
if (TextUtils.isEmpty(receivedDate)) {
return "";
}
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(givenFormat);
Date newDate = null;
try {
newDate = simpleDateFormat.parse(receivedDate);
} catch (ParseException e) {
e.printStackTrace();
}
simpleDateFormat = new SimpleDateFormat(expectedFormat);
receivedDate = simpleDateFormat.format(newDate);
return receivedDate;
}
/**
* #param dateString
* #return passed string date in different locale, My Db is in IST so I an converting IST in different locale
*/
public String getTimeInSelectedLocale(String dateString) {
if (TextUtils.isEmpty(dateString)) {
return dateString;
}
SimpleDateFormat sdf = new SimpleDateFormat(expectedFormat);
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata")); //We want Indian time to change in different locale, so this line is compulsory, you can replace with your country
Date date1 = null;
try {
date1 = sdf.parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
}
//below this line -> TimeZone.getTimeZone(TimeZone.getDefault().getID(), will return current locale for example for India "Asia/Kolkata" for UAE "Asia/Dubai"
sdf.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID())); //This line chooses current local in which you want time
String localDate = sdf.format(date1);
return localDate;
}
//I am converting the IST time "2018-08-14T07:00:00:00" into UAE(Duabai) "14-08-2018 05:30:00" and other countries
I believe what you're looking for is the Joda library. It has functionality better than the Calendar or Date classes as specified in the answer here.
This function should be particularly useful.
Related
I have string of the pattern "yyyy-MM-dd'T'HH:mm:ssZ" which I want to convert to ZonedDateTime format using Java.
Input String Example: "2019-11-23T10:32:15+12:24"
Output: ZonedDateTime
Edit: I have tried this but it does not work.
ZonedDateTime convertToZonedDateTime(final String source) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = dateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
I have this solution which works for string "2018-04-05 19:58:55" produces output 2018-04-05T19:58:55+05:30[Asia/Kolkata] but when I change the pattern in function to "yyyy-MM-dd'T'HH:mm:ssZ" and input string to 2019-11-23T10:32:15+12:24 it does not work due to ParseException: Unparsable data.
I need ZonedDateTime format for an API which expects the input time in that format.
tl;dr
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
Semantically, we are done at this point with a OffsetDateTime object in hand.
But you claim to be using an API that demands a ZoneDateTime object. We have no known time zone to apply, so let’s apply UTC (an offset of zero hours-minutes-seconds).
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
.atZoneSameInstant( // Convert from `OffsetDateTime` to `ZonedDateTime` by applying a time zone.
ZoneOffset.UTC // This constant is a `ZoneOffset` object, whose class extends from `ZoneId`. So we can use it as a time zone, though semantically we are making a mess.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text in standard ISO 8601 format.
See this code run live at IdeOne.com.
2019-11-22T22:08:15Z
Caveat: The offset on your example input string looks wrong to me.
Details
You need to understand some concepts for date-time handling.
Offset
A offset-from-UTC is merely a number of hours-minutes-seconds ahead of, or behind, the meridian line drawn at the Greenwich Royal Observatory.
In Java, we represent an offset with the ZoneOffset class. A date and time-of-day in the context of an offset is represented with the OffsetDateTime class. Such an object represents a moment, a specific point on the timeline.
Time zone
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. These changes are determined by politicians. So these changes can be arbitrary and capricious, and happen surprisingly often, often with little or no warning. In North America, for example, most regions have adopted Daylight Saving Time (DST) nonsense, resulting in the offset changing twice a year. Currently there is a fad amongst politicians to quit DST changes while staying permanently year-round on “summer time”, one hour ahead of standard time.
There is a database cataloging these changes. The tZ data is a file maintained by IANA listing changes worldwide. You’ll likely find copies of this data in your host OS, in enterprise-quality database management systems such as Postgres, and in your Java Virtual Machine. Be sure to keep these up-to-date with changes in zones you care about.
Time zones have names in the format of Continent/Region. For example, Africa/Tunis, Europe/Paris, and Asia/Kolkata.
OffsetDateTime
So an input string like "2019-11-23T10:32:15+12:24" has no indicator of time zone, only an offset. So we must parse it an a OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.parse( "2019-11-23T10:32:15+12:24" ) ;
Asking for that as a ZonedDateTime makes no sense. We cannot reliably determine a time zone merely from an offset. Many time zones may share an offset for some pints in time.
Also, that particular input string 2019-11-23T10:32:15+12:24 is suspect. That offset of twelve hours and twenty-four minutes does not map to any current time zone. Are you sure it is correct?
You can convert your OffsetDateTime to a ZonedDateTime by specifying a time zone to use in adjustment. I suggest using UTC. While this works technically, semantically it is confusing. Moments in UTC are best represented by OffsetDateTime rather than ZonedDateTime. But apparently you are interoperating with code that demands a ZonedDateTime specifically, so c’est la vie.
ZonedDateTime zdt = odt.atZoneSameInstant( ZoneOffset.UTC ) ;
Instant
Tip: Generally, APIs should be written to hand off moments as an Instant object, which is always in UTC by definition.
LocalDateTime
You present another string input, "2018-04-05 19:58:55". This input lacks any indicator of time zone or offset-from-UTC. So we cannot know if this means almost-8PM in Tokyo Japan, or almost-8PM in Toulouse France, or almost-8PM in Toledo Ohio US — which are all events happening several hours apart, different points on the time zone.
Such a value must be parsed as a LocalDateTime. Replace the SPACE in the middle with a T to comply with ISO 8601 standard formatting.
LocalDateTime ldt = LocalDateTime.parse( "2018-04-05 19:58:55".replace( " " , "T" ) ) ;
The resulting object does not represent a moment, is not a point in the timeline. Such an object represents potential moments along a spectrum of about 26-27 hours, the range of time zones around the globe.
ZonedDateTime
If you are certain that input string was intended for a particular time zone, apply a ZoneId to get a ZonedDateTime. Then you have determined a moment, a specific point on the timeline.
ZoneId z = ZonedId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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, Java SE 11, and later - 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
Most 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.
You can parse the input date time string into OffsetDateTime and then convert it into ZonedDateTime
String inputDate = "2019-11-23T10:32:15+12:24";
OffsetDateTime offset = OffsetDateTime.parse(inputDate);
ZonedDateTime dateTime = offset.toZonedDateTime();
If you just need ZonedDateTime at same local time with ZoneId then use atZoneSimilarLocal
ZonedDateTime dateTime = offset.atZoneSimilarLocal(ZoneId.systemDefault());
It’s unclear why you think you want a ZonedDateTime, and if you do, in which time zone. A bit of the following has been said already, but I would like to give you three suggestions to choose from:
You don’t need a ZonedDateTime. An OffsetDateTime fits your string better.
If you want a ZonedDateTime in your default time zone, which makes sense, use OffsetDateTime.atZoneSameInstant() (as in the answer by Basil Bourque).
If you just want a ZonedDateTime representation of your string, the one-arg ZonedDateTime.parse() parses it directly.
Use OffsetDateTime
Your string contains an offset, +12:34, and not a time zone, like Pacific/Galapagos. So OffsetDateTime is more correct to represent its content.
String inputStringExample = "2019-11-23T10:32:15+12:24";
OffsetDateTime dateTime = OffsetDateTime.parse(inputStringExample);
System.out.println(dateTime);
Output from this snippet is:
2019-11-23T10:32:15+12:24
I agree with the comment by Basil Bourque, the offset of +12:24 doesn’t look like a real-world UTC offset, but it’s fine for a Stack Overflow example. In 2019 most offsets are on a whole hour and the rest generally on a whole quarter of an hour, so 24 minutes is not used. Historic offsets include many with both minutes and seconds.
I am exploiting the fact that your string is in ISO 8601 format. The classes of java.time parse the most common ISO 8601 variants as their default, that is, without any explicit formatter. Which is good because writing a format pattern string is always error-prone.
Use OffsetDateTime.atZoneSameInstant()
Your call to ZoneId.systemDefault() in the code in the question seems to suggest that you want a ZonedDateTime in your default time zone. On one hand this use of ZonedDateTime seems reasonable and sound. On the other hand relying on ZoneId.systemDefault() is shaky since the default time zone of your JVM can be changed at any time by another part of your program or any ther program running in the same JVM.
ZonedDateTime dateTime = OffsetDateTime.parse(inputStringExample)
.atZoneSameInstant(ZoneId.systemDefault());
System.out.println(dateTime);
Output in my time zone:
2019-11-22T23:08:15+01:00[Europe/Copenhagen]
Parse directly
If you just need a ZonedDateTIme for an API that requires one (for most purposes a poor design), just parse your string into one:
ZonedDateTime dateTime = ZonedDateTime.parse(inputStringExample);
2019-11-23T10:32:15+12:24
Output is indistinguishable from the one we got from OffsetDateTime, but you have got the required type now.
Stay far away from SimpleDateFormat and Date
In the code in your question you tried to use SimpleDateFormat for parsing your string. Since you can use java.time, the modern Java date and time API, stick to it and forget everything about the old date and time classes. The modern API gives you all the functionality you need. In case we had needed a formatter for parsing, the modern DateTimeFormatter would have been the class to use.
What went wrong in your code?
… it does not work due to ParseException: Unparsable data.
Z in your format pattern string is for RFC 822 time zone offset. This is without colon and would have parsed +1224, but not +12:24.
Link
Wikipedia article: ISO 8601
I'm sending date from the Angular app as String to server and converting to java Date object to store in the database.
Also sending timeZoneOffset from UI to use the client's time zone while converting. (After googling I found this approach to get the proper result based on the user location)
Written the following code to convert:
public static void main(String args[]) throws ParseException {
String inputDate = "04/05/2018"; // This date coming from UI
int timeZoneOffset = -330; // This offset coming from UI
// (new Date().getTimeZoneOffset())
getDate(inputDate, timeZoneOffset);
}
public static Date getDate(String inputDate, int timeZoneOffset)
throws ParseException {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(-timeZoneOffset * 60);
System.out.println("Default time zone: " + TimeZone.getDefault().getID());
TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);
System.out.println("Time zone from offset: " + timeZone.getID());
dateFormat.setTimeZone(timeZone);
Date date = dateFormat.parse(inputDate);
System.out.println("Converted date: " + date);
return date;
}
Expected output:
Default time zone: America/New_York
Time zone from offset: GMT+05:30
Converted date: Thu April 5 00:00:00 IST 2018
Actual result in server:
Default time zone: America/New_York
Time zone from offset: GMT+05:30
Converted date: Wed April 4 14:30:00 EDT 2018
Why is the date decreasing to one day even I set the users time zone? I'm new to Date and Time related concepts and I googled a couple of times didn't find answer, could someone please help on this.
Thanks in advance
The Answer by Godfrey is correct.
tl;dr
LocalDate.parse(
"04/05/2018" ,
DateTimeFormatter.ofPattern( "MM/dd/uuuu" )
)
.atStartOfDay(
ZoneId.of( "Asia/Kolkata" )
)
.toString()
2018-04-05T00:00+05:30[Asia/Kolkata]
For storage in your database, use UTC.
When a new day starts in India, the date at UTC is still “yesterday”, so April 4th rather than April 5th. Same moment, same point on the timeline, different wall-clock time.
LocalDate.parse(
"04/05/2018" ,
DateTimeFormatter.ofPattern( "MM/dd/uuuu" )
)
.atStartOfDay(
ZoneId.of( "Asia/Kolkata" )
)
.toInstant()
2018-04-04T18:30:00Z
java.time
You are using terrible old date-time classes that have proven to be poorly designed, confusing, and troublesome. They are now supplanted by the java.time classes.
Avoid legacy date-time classes entirely
ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(-timeZoneOffset * 60);
…
TimeZone timeZone = TimeZone.getTimeZone(zoneOffset);
You are mixing the modern classes (ZoneOffset) with the troublesome legacy classes (TimeZone). Do not mix the modern classes with the legacy classes. Forget all about the old classes including Date, Calendar, and SimpleDateFormat. The java.time classes are designed to entirely supplant the legacy classes.
Instead of TimeZone, use ZoneId (and ZoneOffset).
LocalDate
Parse your input string as a LocalDate. The LocalDate class represents a date-only value without time-of-day and without time zone.
String input = "04/05/2018" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "MM/dd/uuuu" ) ;
LocalDate ld = LocalDate.parse( input , f ) ;
Offset versus Time Zone
int timeZoneOffset = -330;
An offset-from-UTC is not a time zone. An offset is simply a number of hours, minutes, and seconds of displacement from UTC. Your choice of variable name indicates possible confusion on this point.
ZoneOffset offset = ZoneOffset.of( -3 , 30 ) ;
A time zone is a history of past, present, and future changes in offset used by the people of a particular region. So a time zone is always preferable to an offset.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ; // India time zone. Currently uses offset of +05:30 (five and a half hours ahead of UTC).
First moment of the day
You seem to be aiming for the first moment of that date in that zone. Let java.time determine that first-moment-of-the-day. Do not assume that time is 00:00:00. In some zones on some dates, the day may start at another time such as 01:00:00.
ZonedDateTime zdt = ld.atStartOfDay( z ) ; // Determine the first moment of the day on this date in this zone. Not always 00:00:00.
As an example of why you should be using time zones rather than mere offset-from-UTC, look at your example data of -330 which I might easily misinterpret to be three and a half hours behind UTC. This offset is currently only used in the zone America/St_Johns, and only used there for part of the year. So if you applied an offset of -03:30 to a date in the wrong part of the year, your results would be invalid yet go undetected.
Using offset (not recommended)
But your example lacks time zone, so let’s go with offset-from-UTC rather than zone.
Your use of an int integer number to represent an offset-from-UTC is a poor choice of types. First of all, it is ambiguous. That -330 might be interpreted to be a clumsy attempt at -03:30 offset of three and a half hours behind schedule. Secondly, it makes parsing trickier than need be. Thirdly, as a number of minutes, it ignores the possibility of an offset with seconds. Fourthly, you use a negative number for an offset ahead of UTC (apparently) despite common usage and standard usage being the opposite. Lastly, it ignores the clear standard set by ISO 8601 for representing offsets as text: ±HH:MM:SS (and variations). By the way, the padding zero is optional in the standard, but I recommend always including because various libraries and protocols expect it.
Your intent appears to be a number of minutes intended by the integer number.
long seconds =( TimeUnit.MINUTES.toSeconds( - 330 ) * -1 ); // Multiply by negative one to flip the sign to standard ISO 8601 usage, where `+` means “ahead* of UTC and `-` means *behind* UTC.
seconds: 19800
ZoneOffset offset = ZoneOffset.ofTotalSeconds( ( int ) seconds );
offset.toString(): +05:30
Last step: get the first moment of the day in this offset. Caveat: We do not know for certain if this offset is valid on this date, as we lack a time zone.
Convert from the returned ZonedDateTime to an OffsetDateTime. As discussed above, determining first moment of day should always be done with a time zone, and thereby get a ZonedDateTime. We are violating that sensible practice to use an offset, but using the returned ZonedDateTime object would be misleading as ours would lack a true time zone, and have only a mere offset. So the OffsetDateTime class makes our intentions clear and our code more self-documenting.
OffsetDateTime odt = ld.atStartOfDay( offset ).toOffsetDateTime();
Again, this approach using offset is not recommending, as you should be instead gathering a time zone name from the user as input rather than an offset.
UTC
Generally best to store moments in UTC.
Extract a Instant from your OffsetDateTime or ZonedDateTime to get the same moment as UTC.
Instant instant = zdt.toInstant() ;
2018-04-04T18:30:00Z
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, 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.
It's not decreasing by one day, it is decreasing by 11.5 hours. That happens to be the time difference between GMT+05:30 and "America/New_York", which is GMT-04:30 or GMT-05:30 (depending on time of year).
GMT+05:30 is somewhere in India, I think, since that is about the only place to use a 30 minute offset rather than a whole hour. When it is April 5th in India, it is still April 4th in New York.
The problem may be you aren't getting a time from the client, so it will assume midnight. If you are doing time zone conversion, it is best to include the actual time.
I've seen many examples of converting date from one time zone to another. But all of them has output as a String. I want a date object as output.
The methods I've tried -
Approach 1
SimpleDateFormat dateTimeFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z");
dateTimeFormat.setTimeZone(TimeZone.getTimeZone("Asia/Calcutta"));
Date date = new Date();
System.out.println(dateTimeFormat.format(date)); // this print IST Timezone
DateFormat timeFormat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z");
timeFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String estTime = timeFormat.format(date);
try {
date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z", Locale.ENGLISH).parse(estTime);
System.out.println(date);
} catch (ParseException ex) {
Logger.getLogger(A.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(timeFormat.format(date));
Approach 2
private static Date shiftTimeZone(Date date, TimeZone sourceTimeZone, TimeZone targetTimeZone) {
System.out.println(sourceTimeZone.toString());
System.out.println(targetTimeZone.toString());
Calendar sourceCalendar = Calendar.getInstance();
sourceCalendar.setTime(date);
sourceCalendar.setTimeZone(sourceTimeZone);
Calendar targetCalendar = Calendar.getInstance();
for (int field : new int[]{Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND}) {
targetCalendar.set(field, sourceCalendar.get(field));
}
targetCalendar.setTimeZone(targetTimeZone);
return targetCalendar.getTime();
}
Approach 1 gives me result as a String.
03/22/2018 10:16:57 AM EDT <- instanceOf String
Approach 2 gives me correct date and time of Eastern Time time zone, but the Date has the time zone of IST.
Thu Mar 22 10:16:57 IST 2018 <- instanceof Date
Can anyone please help me to obtain a Date object with Eastern Time TimeZone.
Update - My ultimate goal is to get Unix Timestamp of the current Eastern Time.
tl;dr
Instant.now() // Capture the current moment in UTC, an `Instant` object.
.atZone( // Adjust from UTC into a particular time zone.
ZoneId.of( “Asia/Kolkata” )
) // Returns a `ZonedDateTime` object.
.withZoneSameInstant( // Adjust into yet another time zone. All three are the same moment but vowed using different wall-clock times.
ZoneId.of( “Africa/Tunis” )
) // Returns another `ZonedDateTime` object.
Or…
ZonedDateTime.now(
ZoneId.of( “Asia/Kolkata” )
).withZoneSameInstant(
ZoneId.of( “Africa/Tunis” )
)
Avoid legacy date-time classes
Firstly, stop using the legacy date-time classes. They are an awful wretched mess. Supplanted by the java.time classes.
Date is replaced by Instant.
Calendar is replaced by ZonedDateTime
SimpleDateFormat is replaced by DateTimeFormatter.
Deceived by Date::toString
Secondly, understand that Date has a horribly confusing feature of dynamically applying your JVM’s current default time zone while generating a String. Date always represents a moment in UTC. The toString method creates a false illusion of Date carrying a time zone, when actually its value is in UTC. While well-intentioned by the class designers, this was a disastrous decision, causing no end of confusion amongst countless programmers for decades now.
Even worse: There actually is a time zone buried in a Date, but is irrelevant to this discussion. Confusing? Yes; as I said, an awful wretched mess of bad design.
Instant
The Instant class replacing Date is much clearer. An Instant represents a moment, a point on the timeline, always in UTC, with a resolution of nanoseconds.
Use Instant to capture the current moment in UTC. The JVM’s current default time zone is irrelevant. The host OS’ assigned time zone is irrelevant.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
Unlike Date::toString, the Instant::toString method tells the truth. A Instant is always in UTC, so toString always reports UTC. A String is generated in standard ISO 8601 format. The Z on the end is short for Zulu and means UTC.
instant.toString(): 2018-01-23T12:34:56.123456789Z
About capturing the current moment… In Java 8, the current moment was captured in milliseconds even though the java.time classes can represent nanoseconds. In Java 9 and later, a new implementation of Clock provides for capturing the current moment in finer granularity. In Java 9.0.4 on macOS Sierra, I see microseconds. The hardware clocks on conventional computers nowadays cannot capture the current moment with accuracy beyond microseconds.
ZonedDateTime
To view that same moment through the lens of a wall-clock time used by the people of a particular region, assign that region’s time zone. Applying a ZoneId to an Instant produces a ZonedDateTime. Conceptually:
ZonedDateTime = ( Instant + ZoneId )
In code:
ZoneId z = ZoneId.of( “Pacific/Auckland” ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
Adjusting to another time zone is easy. You can start with the Instant again.
ZoneId zKolkata = ZoneId.of( “Asia/Kolkata” ) ;
ZonedDateTime zdt = instant.atZone( zKolkata ) ;
Or you can adjust the ZonedDateTime object. The java.time classes use immutable objects. So rather than “mutate” (alter) the original object, the adjustment produces a new distinct object.
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( zKolkata ) ; // Same moment, same point on the timeline, different wall-clock time.
You can skip the use of the Instant. I do not recommend doing so. Programmers should be doing their thinking, debugging, logging, exchanging of data, and much of their business logic in UTC. So Instant should be your go-to class whenever you start any work with date-time values.
ZonedDateTime zdtNewYork = ZonedDateTime.now( ZoneId.of( "America/New_York" ) ) ;
The ZonedDateTime::toString method wisely extends the ISO 8601 standard by appending the name of the time zone in square brackets.
String output = zdtNewYork.toString() ;
2018-01-23T07:34:56.123456789-05:00[America/New_York]
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, 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.
I have several time zone strings in UTC format, such as "UTC+08:00", "UTC-05:00", the question is how can i convert these utc format strings to the java.util.TimeZone in Java?
I have tried to convert by ZoneId as follows, but it did not work:
ZoneId zoneId = ZoneId.of("UTC+08:00");
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
I know TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); would work, but I do not know the mapping between "UTC+08:00" and "Asia/Shanghai"
tl;dr
Do not use TimeZone class (now legacy).
Use ZoneOffset and ZoneId instead.
Example:
ZoneOffset.of( "+08:00" )
Use java.time.ZoneId, not TimeZone
The troublesome old date-time classes bundled with the earliest versions of Java are now legacy, supplanted by the java.time classes. Among these old legacy classes is TimeZone, now supplanted by ZoneId and ZoneOffset.
An offset-from-UTC is a number of hours and minutes adjustment ahead of, or behind, UTC. This is represented by the ZoneOffset class.
A time zone is a collection of offsets, the history of changes in the offset used by a particular region in determining their wall-clock time. This is represented by the ZoneId class.
Using a time zone is always preferable to an offset as a zone has the offset plus so much more information. But your examples are only mere offsets. So use the ZoneOffset to parse the strings after deleting the characters UTC.
String input = "UTC+08:00".replace( "UTC" , "" ) ;
ZoneOffset offset = ZoneOffset.of( input ) ;
Do not guess the time zone
You cannot assume that a particular offset implies a particular time zone. Many zones may have used a particular offset in the past, present, or future. So you should not guess the zone.
Take, for example, the offset of +08:00. That offset is currently used by several different time zones including Asia/Shangai, Asia/Macao, and Australia/Perth.
If you are certain a particular zone was intended for a date-time value, apply it to get a ZonedDateTime. But do not guess.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.now() ;
ZoneId z = ZoneId.of( "Asia/Shanghai" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
If you do not know for certain the intended time zone and have only an offset, use the offset to get an OffsetDateTime.
Instant instant = Instant.now() ;
ZoneOffset offset = ZoneOffset.of( "+08:00" ) ;
OffsetDateTime odt = instant.atOffset( offset ) ;
Convert
Best to avoid the old legacy class TimeZone. But if you must use that class to work with old code not yet updated for the java.time classes, you can convert to/from a ZoneId. Use the new conversion methods added to the old classes.
TimeZone myLegacyTimeZone = TimeZone.getTimeZone( myModernZoneId );
…and…
ZoneId z = myLegacyTimeZone.toZoneId() ;
Note that ZoneOffset is a subclass of ZoneId. Normally, we ignore that inheritance relationship. If you have only a mere offset such as +08:00, use ZoneOffset. If you have a full time zone such as Asia/Shanghai, use ZoneId. One exception to this rule is for this conversion to/from TimeZone where only the superclass ZoneId is recognized.
If you strip the UTC, you can parse it as a ZoneOffset, which extends ZoneId
ZoneId zoneId = ZoneOffset.of("+08:00")
Since you can use the modern classes in the java.time package, I recommend you stick with them and avoid the outdated classes like TimeZone, SimpleDateFormat and Date. I am mostly repeating what #Basil Bourque already said in his answer, but also wanted to demonstrate how nicely his suggestion fits into your context:
DateTimeFormatter format = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
ZonedDateTime dateTime = LocalDateTime.parse(dateTimeString, format).atZone(zoneId);
Instant i = dateTime.toInstant();
System.out.println(dateTime + " -> " + i);
I have also demonstrated that you may convert the ZonedDateTime to an Instant in case you need that. The snippet prints
2017-05-05T05:05+08:00[UTC+08:00] -> 2017-05-04T21:05:00Z
If you are sure your date-time string and your zone string belong together, there is no need to go through String.replace() for removing UTC from the beginning of the zone string.
I am parsing the string independently of the time zone and then combining it with the zone offset information afterward. I think it’s more natural than having to know the zone for parsing.
In case you need an oldfashioned Date, for example for a call to some legacy code, that’s easy enough:
Date d = Date.from(i);
The old classes are troublesome
Even though I know the old classes have a tendency to show unwanted behaviour without telling you that anyting is wrong, I was still negatively surprised to learn that the code in your question didn’t work. It gives a time zone of GMT! It’s documented that this is a possibility, though, in the documentation of TimeZone.getTimeZone(ZoneId):
Returns:
the specified TimeZone, or the GMT zone if the given ID cannot be understood.
One may stil wonder how a simple time zone like UTC+08:00 can be “not understood”.
I know that:
Instant is rather a "technical" timestamp representation (nanoseconds) for computing.
LocalDateTime is rather date/clock representation including time-zones for humans.
Still in the end IMO both can be taken as types for most application use cases. As an example: currently, I am running a batch job where I need to calculate the next run based on dates and I am struggling to find pros/cons between these two types (apart from the nanosecond precision advantage of Instant and the time-zone part of LocalDateTime).
Can you name some application examples where only Instant or LocalDateTime should be used?
Edit: Beware of misread documentations for LocalDateTime regarding precision and time-zone.
tl;dr
Instant and LocalDateTime are two entirely different animals: One represents a moment, the other does not.
Instant represents a moment, a specific point in the timeline.
LocalDateTime represents a date and a time-of-day. But lacking a time zone or offset-from-UTC, this class cannot represent a moment. It represents potential moments along a range of about 26 to 27 hours, the range of all time zones around the globe. A LocalDateTime value is inherently ambiguous.
Incorrect Presumption
LocalDateTime is rather date/clock representation including time-zones for humans.
Your statement is incorrect: A LocalDateTime has no time zone. Having no time zone is the entire point of that class.
To quote that class’ doc:
This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.
So Local… means “not zoned, no offset”.
Instant
An Instant is a moment on the timeline in UTC, a count of nanoseconds since the epoch of the first moment of 1970 UTC (basically, see class doc for nitty-gritty details). Since most of your business logic, data storage, and data exchange should be in UTC, this is a handy class to be used often.
Instant instant = Instant.now() ; // Capture the current moment in UTC.
OffsetDateTime
The class OffsetDateTime class represents a moment as a date and time with a context of some number of hours-minutes-seconds ahead of, or behind, UTC. The amount of offset, the number of hours-minutes-seconds, is represented by the ZoneOffset class.
If the number of hours-minutes-seconds is zero, an OffsetDateTime represents a moment in UTC the same as an Instant.
ZoneOffset
The ZoneOffset class represents an offset-from-UTC, a number of hours-minutes-seconds ahead of UTC or behind UTC.
A ZoneOffset is merely a number of hours-minutes-seconds, nothing more. A zone is much more, having a name and a history of changes to offset. So using a zone is always preferable to using a mere offset.
ZoneId
A time zone is represented by the ZoneId class.
A new day dawns earlier in Paris than in Montréal, for example. So we need to move the clock’s hands to better reflect noon (when the Sun is directly overhead) for a given region. The further away eastward/westward from the UTC line in west Europe/Africa the larger the offset.
A time zone is a set of rules for handling adjustments and anomalies as practiced by a local community or region. The most common anomaly is the all-too-popular lunacy known as Daylight Saving Time (DST).
A time zone has the history of past rules, present rules, and rules confirmed for the near future.
These rules change more often than you might expect. Be sure to keep your date-time library's rules, usually a copy of the 'tz' database, up to date. Keeping up-to-date is easier than ever now in Java 8 with Oracle releasing a Timezone Updater Tool.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
Time Zone = Offset + Rules of Adjustments
ZoneId z = ZoneId.of( “Africa/Tunis” ) ;
ZonedDateTime
Think of ZonedDateTime conceptually as an Instant with an assigned ZoneId.
ZonedDateTime = ( Instant + ZoneId )
To capture the current moment as seen in the wall-clock time used by the people of a particular region (a time zone):
ZonedDateTime zdt = ZonedDateTime.now( z ) ; // Pass a `ZoneId` object such as `ZoneId.of( "Europe/Paris" )`.
Nearly all of your backend, database, business logic, data persistence, data exchange should all be in UTC. But for presentation to users you need to adjust into a time zone expected by the user. This is the purpose of the ZonedDateTime class and the formatter classes used to generate String representations of those date-time values.
ZonedDateTime zdt = instant.atZone( z ) ;
String output = zdt.toString() ; // Standard ISO 8601 format.
You can generate text in localized format using DateTimeFormatter.
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( Locale.CANADA_FRENCH ) ;
String outputFormatted = zdt.format( f ) ;
mardi 30 avril 2019 à 23 h 22 min 55 s heure de l’Inde
LocalDate, LocalTime, LocalDateTime
The "local" date time classes, LocalDateTime, LocalDate, LocalTime, are a different kind of critter. The are not tied to any one locality or time zone. They are not tied to the timeline. They have no real meaning until you apply them to a locality to find a point on the timeline.
The word “Local” in these class names may be counter-intuitive to the uninitiated. The word means any locality, or every locality, but not a particular locality.
So for business apps, the "Local" types are not often used as they represent just the general idea of a possible date or time not a specific moment on the timeline. Business apps tend to care about the exact moment an invoice arrived, a product shipped for transport, an employee was hired, or the taxi left the garage. So business app developers use Instant and ZonedDateTime classes most commonly.
So when would we use LocalDateTime? In three situations:
We want to apply a certain date and time-of-day across multiple locations.
We are booking appointments.
We have an intended yet undetermined time zone.
Notice that none of these three cases involve a single certain specific point on the timeline, none of these are a moment.
One time-of-day, multiple moments
Sometimes we want to represent a certain time-of-day on a certain date, but want to apply that into multiple localities across time zones.
For example, "Christmas starts at midnight on the 25th of December 2015" is a LocalDateTime. Midnight strikes at different moments in Paris than in Montréal, and different again in Seattle and in Auckland.
LocalDate ld = LocalDate.of( 2018 , Month.DECEMBER , 25 ) ;
LocalTime lt = LocalTime.MIN ; // 00:00:00
LocalDateTime ldt = LocalDateTime.of( ld , lt ) ; // Christmas morning anywhere.
Another example, "Acme Company has a policy that lunchtime starts at 12:30 PM at each of its factories worldwide" is a LocalTime. To have real meaning you need to apply it to the timeline to figure the moment of 12:30 at the Stuttgart factory or 12:30 at the Rabat factory or 12:30 at the Sydney factory.
Booking appointments
Another situation to use LocalDateTime is for booking future events (ex: Dentist appointments). These appointments may be far enough out in the future that you risk politicians redefining the time zone. Politicians often give little forewarning, or even no warning at all. If you mean "3 PM next January 23rd" regardless of how the politicians may play with the clock, then you cannot record a moment – that would see 3 PM turn into 2 PM or 4 PM if that region adopted or dropped Daylight Saving Time, for example.
For appointments, store a LocalDateTime and a ZoneId, kept separately. Later, when generating a schedule, on-the-fly determine a moment by calling LocalDateTime::atZone( ZoneId ) to generate a ZonedDateTime object.
ZonedDateTime zdt = ldt.atZone( z ) ; // Given a date, a time-of-day, and a time zone, determine a moment, a point on the timeline.
If needed, you can adjust to UTC. Extract an Instant from the ZonedDateTime.
Instant instant = zdt.toInstant() ; // Adjust from some zone to UTC. Same moment, same point on the timeline, different wall-clock time.
Unknown zone
Some people might use LocalDateTime in a situation where the time zone or offset is unknown.
I consider this case inappropriate and unwise. If a zone or offset is intended but undetermined, you have bad data. That would be like storing a price of a product without knowing the intended currency (dollars, pounds, euros, etc.). Not a good idea.
All date-time types
For completeness, here is a table of all the possible date-time types, both modern and legacy in Java, as well as those defined by the SQL standard. This might help to place the Instant & LocalDateTime classes in a larger context.
Notice the odd choices made by the Java team in designing JDBC 4.2. They chose to support all the java.time times… except for the two most commonly used classes: Instant & ZonedDateTime.
But not to worry. We can easily convert back and forth.
Converting Instant.
// Storing
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
// Retrieving
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;
Converting ZonedDateTime.
// Storing
OffsetDateTime odt = zdt.toOffsetDateTime() ;
myPreparedStatement.setObject( … , odt ) ;
// Retrieving
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = odt.atZone( z ) ;
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.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
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. Hibernate 5 & JPA 2.2 support java.time.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 brought some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android (26+) bundle implementations of the java.time classes.
For earlier Android (<26), a process known as API desugaring brings a subset of the java.time functionality not originally built into Android.
If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. 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.
One main difference is the "Local" part of LocalDateTime. If you live in Germany and create a LocalDateTime instance and someone else lives in the USA and creates another instance at the very same moment (provided the clocks are properly set) - the value of those objects would actually be different. This does not apply to Instant, which is calculated independently from a time zone.
LocalDateTime stores date and time without a timezone, but its initial value is timezone-dependent. Instant's is not.
Moreover, LocalDateTime provides methods for manipulating date components like days, hours, and months. An Instant does not.
apart from the nanosecond precision advantage of Instant and the
time-zone part of LocalDateTime
Both classes have the same precision. LocalDateTime does not store the timezone. Read Javadocs thoroughly, because you may make a big mistake with such invalid assumptions: Instant and LocalDateTime.
You are wrong about LocalDateTime: it does not store any time-zone information and it has nanosecond precision. Quoting the Javadoc (emphasis mine):
A date-time without a time-zone in the ISO-8601 calendar system, such as 2007-12-03T10:15:30.
LocalDateTime is an immutable date-time object that represents a date-time, often viewed as year-month-day-hour-minute-second. Other date and time fields, such as day-of-year, day-of-week and week-of-year, can also be accessed. Time is represented to nanosecond precision. For example, the value "2nd October 2007 at 13:45.30.123456789" can be stored in a LocalDateTime.
The difference between the two is that Instant represents an offset from the Epoch (01-01-1970) and, as such, represents a particular instant on the time-line. Two Instant objects created at the same moment in two different places on the Earth will have exactly the same value.
LocalDateTime has no time-zone information: one LocalDateTime may represent different instants for different machines around the world. So you should not try to use it with an implicit time-zone (the system's default one). You should use it for what it represents, for instance "New-year is January 1st, at 0:00": this means a different time at all points on the globe but it's wanted in this case.
Instant is a point in time at the Greenwich time-zone. Use it in addition to the user's time-zone to show him/her the start of a meeting in his/her time-zone, for instance.
If these two classes do not represent what you wanted to store/exchange, then maybe ZonedDateTime or another class may do a better job.
Here is a simple synthetic schema to get the big-picture of the classes in the java.time package and their relation to the ISO-8601 standard used to reliably and effortlessly exchange dates and times between Java and other languages or frameworks:
The schema is explained in details here: http://slaout.linux62.org/java-date-time/