Jesper has already given you short answers to your questions.
The no-arg method LocalDate.html#now obtains the current date from the system clock in the default (system) time-zone. Therefore, when you print LocalDate.now() in Madrid, India and New York, at the same time, you may get different results (depending on what time you do it).
Demo:
import java.time.LocalDate;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
System.out.println(LocalDate.now(ZoneId.of("Europe/Madrid")));
System.out.println(LocalDate.now(ZoneId.of("Asia/Kolkata")));
System.out.println(LocalDate.now(ZoneId.of("America/New_York")));
}
}
Output:
2022-09-28
2022-09-29
2022-09-28
Note: to understand this difference, check the output of the following program:
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
System.out.println(LocalDateTime.now(ZoneId.of("Europe/Madrid")));
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Kolkata")));
System.out.println(LocalDateTime.now(ZoneId.of("America/New_York")));
}
}
Result:
2022-09-28T21:03:56.438167
2022-09-29T00:33:56.443577
2022-09-28T15:03:56.444049
However, a LocalDate object does not store the time-zone information. Therefore, when you print an object of LocalDate (i.e. LocalDate#toString), the printed value will remain the same irrespective of the system time-zone.
A java.util.Date object too does not store the time-zone information. It stores the number of milliseconds from January 1, 1970, 00:00:00 GMT. However, when you print an object of java.util.Date (i.e. Date#toString), it uses the default (system) time-zone to print the value i.e. if you print an object of java.util.Date on systems with different time-zones set to them, you will get different results.
Jesper has already given you short answers to your questions.
The no-arg method LocalDate.html#now obtains the current date from the system clock in the default (system) time-zone. Therefore, when you print LocalDate.now() in Madrid, India and New York, at the same time, you may get different results (depending on what time you do it).
Demo:
import java.time.LocalDate;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
System.out.println(LocalDate.now(ZoneId.of("Europe/Madrid")));
System.out.println(LocalDate.now(ZoneId.of("Asia/Kolkata")));
System.out.println(LocalDate.now(ZoneId.of("America/New_York")));
}
}
Output:
2022-09-28
2022-09-29
2022-09-28
Note: to understand this difference, check the output of the following program:
import java.time.LocalDateTime;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
System.out.println(LocalDateTime.now(ZoneId.of("Europe/Madrid")));
System.out.println(LocalDateTime.now(ZoneId.of("Asia/Kolkata")));
System.out.println(LocalDateTime.now(ZoneId.of("America/New_York")));
}
}
Result:
2022-09-28T21:03:56.438167
2022-09-29T00:33:56.443577
2022-09-28T15:03:56.444049
However, a LocalDate object does not store the time-zone information. Therefore, when you print an object of LocalDate (i.e. LocalDate#toString), the printed value will remain the same irrespective of the system time-zone.
A java.util.Date object too does not store the time-zone information. It stores the number of milliseconds from January 1, 1970, 00:00:00 GMT. However, when you print an object of java.util.Date (i.e. Date#toString), it uses the default (system) time-zone to print the value i.e. if you print an object of java.util.Date on systems with different time-zones set to them, you will get different results.
I don't think Question # 4 was answered well. I think it is obvious to Jasper and others but not so obvious to me and I think OP.
LocalDateTime.now() will use either system clock or provided timezone to determine what "now" is, but then removes the time zone reference. It is impossible to determine what original time zone was used to create the LocalDateTime object.
That had me wondering how does LocalDateTime.atZone(ZoneId.of("Europe/Madrid")) properly convert the LocalDateTime to Europe/Madrid timezone if there is no time zone reference point to convert from? For example, if LocalDateTime is 2022-09-28T21:03:56, how can it then convert that to Europe/Madrid when it does not understand what time zone the date/time it holds originated from?
The solution is obvious now. It doesn't convert and it does not try to figure out what timezone it originated from. It just appends the timezone specified to the date/time it has.
So if a LocalDateTime object has a value of 2022-09-28T21:03:56, then LocalDateTime.atZone(ZoneId.of("Europe/Madrid")) instantiates a ZonedDateTime object with a value of 2022-09-28T21:03:56+01:00. Notice there is no change other than adding the +01:00 offset in use by the people of that time zone at that moment.
If that particular time-of-day does not exist in that time zone at that moment, such as during a Daylight Saving Time (DST) cutover, the time-of-day in the new ZonedDateTime is adjusted logically.
After I understood those two points it was all really clear how it worked.
Hope this helps someone else that this was not that obvious for.
Since Java 8 the date/time API is pretty easy to work with.
In your case:
// your local date/time with no timezone information
LocalDateTime localNow = LocalDateTime.now();
// setting UTC as the timezone
ZonedDateTime zonedUTC = localNow.atZone(ZoneId.of("UTC"));
// converting to IST
ZonedDateTime zonedIST = zonedUTC.withZoneSameInstant(ZoneId.of("Asia/Kolkata"));
You'll see a difference in the time (and possibly the date) between zonedUTC and zonedIST, reflecting the time zone offset between the two.
Note the usage of withZoneSameInstant here, e.g. as opposed to withZoneSameLocal.
tl;dr
Use Instant, not LocalDateTime for tracking a moment.
Instant // Represents a moment in UTC with a resolution of nanoseconds.
.ofEpochSecond(
myCountOfWholeSecondsSinceStartOf1970UTC // Internally, time is tracked as a count of seconds since 1970-01-01T00:00Z plus a fractional second as nanoseconds.
) // Returns a moment in UTC.
.atZone( // Adjust from UTC to another time zone. Same moment, different wall-clock time.
ZoneId.of( "Asia/Kolkata" ) // Specify time zone name in `Continent/Region` format, never 2-4 letter pseudo-zone.
) // Returns a `ZonedDateTime` object.
.toString() // Generate a string representing the value of this `ZonedDateTime` in standard ISO 8601 format extended to append the name of the time zone in square brackets.
Wrong class
LocalDateTime dateTimeOfAfterFiveDays = LocalDateTime.ofEpochSecond(after5,0,ZoneOffset.UTC);
LocalDateTime is the wrong class to use here. This class cannot represent a moment. It lacks any concept of time zone or offset-from-UTC. A LocalDateTime holds just a date and a time-of-day, say noon on the 23rd of January this year. But we have no idea if what was intended was noon in Tokyo, noon in Kolkata, noon in Paris, or noon in Montréal — all of these are hours apart, very different moments.
Instant
To represent a moment in UTC, use Instant.
Apparently you have a count of whole seconds since the epoch reference of first moment of 1970 in UTC.
Instant instant = Instant.ofEpochSecond( count ) ;
ZoneId & ZonedDateTime
To see this value through the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime.
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(!).
ZoneId z = ZoneId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
LocalDateTime is wrong class
Never use LocalDateTime to represent a moment, a specific point on the timeline. Purposely lacking any concept of time zone or offset-from-UTC, this type represents potential moments along the range of about 26-27 hours (range of time zones around the globe).
Current moment
To get the current moment in UTC, use Instant.
Instant instant = Instant.now() ;
To get the current moment as seen in the wall-clock time used by people in a particular region (a time zone), use ZonedDateTime.
I suspect your problem is that your expected time zone was not actually the current default zone when your code ran. In your code you failed to specify a time zone, and so the JVM’s current default time zone was silently applied. You could verify the current default by calling ZoneId.systemDefault().toString().
Relying implicitly on the JVM’s current default time zone is a bad practice in my opinion. Better to always specify your desired/expected time zone explicitly. Always pass the optional ZoneId argument.
ZoneId z = ZoneId.of( "America/Montreal" ) ; // Or get the JVM’s current default time zone: ZoneId.systemDefault()
ZonedDateTime zdt = ZonedDateTime.now( 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), 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….
java.util.TimeZone.setDefault() is the method in java which is used to set the timeZone
in java. It takes TimeZone as input parameter. You can get an object of TimeZone by TimeZone.getTimeZone("id"); There are different id for different time Zone. For example id for me is "Asia/Calcutta" so passing that will return me the TimeZone of my region.
TimeZone tzone = TimeZone.getTimeZone("Asia/Calcutta");
TimeZone.setDefault(tzone);
Above line will change my timezone to calcutta region.
Offsets vary over time
On April 6, 2020, most time zones on the east coast of North America such as America/Montreal and America/New_York use an offset of four hours behind UTC.
So, in those zones 11 PM on 2020-04-06 is simultaneously 3 AM on the 7th in UTC. Add four hours to 2020-04-06T23:00 to get 2020-04-07T03:00. Adding four hours brings us from the wall-clock time used in America/Port-au-Prince and America/Nassau to UTC.
The offset used by the time zones mentioned above are four hours behind UTC only half the year. The politicians in those locations have decided to observe Daylight Saving Time (DST) for about half the year. So half the year they jump their clocks ahead an hour for an offset-from-UTC of -04:00, and later in the year they fall back an hour for an offset-from-UTC of -05:00. For example, earlier in the year 11 PM in America/New_York would be 04:00 in UTC rather than the 03:00 time seen in April.
Standard time, in January, is five hours behind UTC. So, 11 PM plus five hours is 4 AM next day.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2020 , 1 , 6 , 23 , 0 , 0 , 0 , zNewYork ) ;
Instant instant = zdt.toInstant() ; // 2020-01-07T04:00Z
Daylight Saving Time, in April, is four hours behind UTC. So, 11 PM plus four hours is 3 AM next day.
ZoneId z = ZoneId.of( "America/New_York" ) ;
ZonedDateTime zdt = ZonedDateTime.of( 2020 , 4 , 6 , 23 , 0 , 0 , 0 , zNewYork ) ;
Instant instant = zdt.toInstant() ; // 2020-04-07T03:00Z
By the way, DST is only one of many reasons politicians have for changing the offset used by the time zone(s) of their jurisdiction. Politicians around the world have shown a penchant for changing their offsets surprisingly often. Your programming should always expect a time zone to change its offset. Just because your particular zone of concern does not currently observe DST does not mean the offset will never change.
As for parsing 2020-04-06T23:00:00.000 as a LocalDateTime, you will get 2020-04-06T23:00:00.000. Ignoring time zones is the entire point of LocalDateTime. So no adjustments are made.

You may be thinking of LocalDateTime as representing a particular locality. But, no, it represents any locality or all localities. But never any one particular locality. For a particular locality, use ZonedDateTime.
Pseudo time zones
Another point: EST is not a time zone. Such 2-4 letter letter codes are not real time zones, are not standardized, and are not even unique. For example, by EST, did you mean Australian Eastern Standard Time or North American Eastern Standard Time? Is CST Central Standard Time or China Standard Time? Avoid these pseudo-zones in your date-time-handling.
And you happened to use the wrong pseudo-code. On April 6 2020, most of those east coast time zones are observing Daylight Saving Time (DST). So they would be considered to be in “EDT” rather than “EST”. Avoid the problem by specifying the real time zone name.
Real time zones are named using Continent/Region format. See Wikipedia for a list of real time zones.
Never call LocalDateTime.now
I cannot imagine a case where calling LocalDateTime.now would be the right thing to do. Determining the current moment requires a time zone (or offset). And LocalDateTime by definition has no time zone or offset. When a programmer writes LocalDateTime.now, you can bet they do not fully understand the necessary concepts.
When you call LocalDateTime.now, the JVM’s current default time zone is implicitly used to capture the current time as seen by the people in the region of that zone. And then the fact of that time zone is deleted.
This:
LocalDateTime.now()
…is the same as this:
LocalDateDate.now( ZoneId.systemDefault() )
…which is the same as getting the current moment as seen in a particular time zone, followed by removing the time zone information:
ZonedDateTime.now( ZoneId.systemDefault() ).toLocalDateTime()
For more code examples demonstrating LocalDateTime.now, see the correct Answer by Ole V.V.
Where to use LocalDateTime
If LocalDateTime is not appropriate for getting the current moment, and is not appropriate for tracking any moment, what is the appropriate use for this class? Three things: representing any locality, representing all localities, and booking future appointments.
- Any locality would be something like stating when Christmas starts. This year Christmas starts at 2020-12-25T00:00 wherever you are in the world. Of course this means Christmas starts first in Kiribati after midnight, later in Japan after midnight, even later in Tunisia after midnight, and still later in Chicago after midnight.
- All localities would be something like stating our company policy that lunch breaks at all our factories in Delhi, Düsseldorf, and Detroit are scheduled for 12:30. So on April 6 2020, the break will be at 2020-04-06T12:30:00. This break will occur first in Delhi, several hours later in Düsseldorf, and even more hours later in Detroit.
- Booking future appointments where you intend to keep the same time-of-day regardless of changes to the time zone’s offset must recorded without the offset. If your next dental appointments is in six months at 3 PM, we want to record the 3 PM without regard for the offset. If the offset were to be changed by politicians, we still want the appointment to start when the clock strikes three on that date in that zone.
Set the appointment.
LocalDateTime appointment = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
Determine a moment for producing a calendar. Every time you do this, you may get a different result if the politicians have changed the rules of this time zone.
ZoneId z = ZoneId.of ( "America/Panama" ) ;
ZonedDateTime dueToArrive = appointment.atZone( z ) ;
LocalDate
As for LocalDate, we saw in the examples above that the date depends on time zone. For any given moment, the date varies around the globe by time zone. It may be “tomorrow” in Tokyo Japan while still “yesterday” in Montréal Québec Canada.
So, you must specify a time zone when asking for the current date.
LocalDate ld = LocalDate.now( ZoneId.of( "Asia/Kolkata" ) ) ;
If you omit the time zone, the JVM’s current default time zone is implicitly applied. So LocalDate.now() becomes LocalDate.now( ZoneId.systemDefault() ). I recommend specifying your desired/expected zone explicitly, rather than rely on implicit default.
Server time zone
You said:
server which runs in EST vs UTC
FYI, servers should generally be set to UTC. Most of your thinking, programming, logging, and so on should be done in UTC. Learn to think of UTC as The One True Time, with all other zones but mere variations.
As a programmer, you should never rely on the default time zone. That is far outside your control. That default can be changed so easily by the user or sysadmin. Furthermore, any code in any thread of any app within the JVM can instantly change the JVM’s current default time with a call to TimeZone.setDefault. So even during execution of your app, the default can be changed at any moment.
To prevent confusion pass explicit time zone to now() and see for yourself:
ZoneId easternTime = ZoneId.of("America/Montreal");
System.out.println(LocalDateTime.now(easternTime));
System.out.println(LocalDateTime.now(ZoneOffset.UTC));
System.out.println(LocalDate.now(easternTime));
System.out.println(LocalDate.now(ZoneOffset.UTC));
Output when I ran just now:
2020-04-06T09:56:17.381558 2020-04-06T13:56:17.385215 2020-04-06 2020-04-06
While you are correct that LocalDateTime and LocalDate don’t contain any time zone information, their now methods do use time zones. Either the one passed to them, or if you use the no-arg variant, the default time zone of the JVM.
You also asked:
Also, If I convert below date string to LocalDateTime and then toLocalDate, what would be the outcome?
2020-04-06T23:00:00.000
Why not try out that too?
LocalDateTime ldt = LocalDateTime.parse("2020-04-06T23:00:00.000");
System.out.println(ldt);
LocalDate ld = ldt.toLocalDate();
System.out.println(ld);
2020-04-06T23:00 2020-04-06
Converting from LocalDateTime to LocalDate involves no time zone (or UTC offset) whatsoever. The time part is simply discarded and the date part kept unchanged.
I personally prefer
LocalDateTime.now(ZoneOffset.UTC);
as it is the most readable option.
LocalDateTime does not contain Zone information. ZonedDatetime does.
If you want to convert LocalDateTime to UTC, you need to wrap by ZonedDateTime fist.
You can convert like the below.
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt.toLocalTime());
ZonedDateTime ldtZoned = ldt.atZone(ZoneId.systemDefault());
ZonedDateTime utcZoned = ldtZoned.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println(utcZoned.toLocalTime());