I have a Date as a String - 15Sep20162040, which I have to format it into another format with Timezone as 2016-09-15T20:40:00+0400.
What I did to do it as follows:
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class DateFormatExample {
private static SimpleDateFormat offsetDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZ");
private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
"ddMMMyyyyHHmm");
public static void main(String[] args) throws ParseException {
String date = "15Sep20162040";
String result = offsetDateFormat.format(dateFormatter.parse(date));
System.out.println(result); // 2016-09-15T20:40:00+0400
}
}
Now, I have to modify the output based on timezone difference, for example if difference is +0100, output should resemble as: 2016-09-15T20:40:00+0100 and if difference is -0200, output should resemble as: 2016-09-15T20:40:00-0200.
How can I achieve it?
You can use SimpleDateFormat's setTimeZone method as below:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class DateFormatExample {
private static SimpleDateFormat offsetDateFormat = new SimpleDateFormat(
"yyyy-MM-dd'T'HH:mm:ssZ");
private static SimpleDateFormat dateFormatter = new SimpleDateFormat(
"ddMMMyyyyHHmm");
public static void main(String[] args) throws ParseException {
String date = "15Sep20162040";
String result = offsetDateFormat.format(dateFormatter.parse(date));
System.out.println(result); // 2016-09-15T20:40:00+0400
offsetDateFormat.setTimeZone(TimeZone.getTimeZone("GMT-8:00"));
result = offsetDateFormat.format(dateFormatter.parse(date));
System.out.println(result);
}
}
If you simply want to change the timezone at the end of result, please try the following:
String offset = "GMT-8:00";
String date = "15Sep20162040";
date = date+" "+offset;
SimpleDateFormat dateFormatter2 = new SimpleDateFormat("ddMMMyyyyHHmm Z");
SimpleDateFormat offsetDateFormat2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
offsetDateFormat2.setTimeZone(TimeZone.getTimeZone(offset));
String result = offsetDateFormat2.format(dateFormatter2.parse(date));
System.out.println(result);
Hope this helps.
tl;dr
ZonedDateTime zdt =
LocalDateTime.parse ( "15Sep20162040" ,
DateTimeFormatter.ofPattern ( "ddMMMyyyyHHmm" )
.withLocale( Locale.English )
)
.atZone ( ZoneId.of ( "America/Puerto_Rico" ) );
2016-09-15T20:40-04:00[America/Puerto_Rico]
zdt.atZone( ZoneId.of ( "Pacific/Auckland" ) ) // Same moment viewed through different wall-clock time
2016-09-16T12:40+12:00[Pacific/Auckland]
Using java.time
Avoid the troublesome old date-time classes, now supplanted by the java.time classes.
Define a formatting pattern to match your input string.
String input = "15Sep20162040";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "ddMMMyyyyHHmm" ).withLocale ( Locale.ENGLISH );
By the way, this is a terrible format for a date-time string. It assumes English, abuses English with incorrect abbreviation of month name, and is confusing and ambiguous. Instead, use the standard ISO 8601 formats when serializing date-time values to text.
Un-zoned
Parse the input string as a LocalDateTime since it lacks any info about offset-from-UTC or time zone.
LocalDateTime ldt = LocalDateTime.parse ( input , f );
Understand that without an offset or time zone, this LocalDateTime object has no real meaning. It represents many possible moments, but not a specific point on the timeline. For example, noon in Auckland NZ is a different moment than noon in Kolkata India which is an earlier moment than noon in Paris France.
Assign an offset-from-UTC
You indicate this date-time was intended to be a moment with an offset-from-UTC of four hours behind UTC (-04:00). So next we apply a ZoneOffset to get a OffsetDateTime object.
Tip: Always include the colon and the minutes and padding zeros in your offset-from-UTC strings. While not required by the ISO 8601 standard, common software libraries and protocols expect the fuller formatting.
ZoneOffset offset = ZoneOffset.ofHours( -4 );
OffsetDateTime odt = ldt.atOffset( offset );
Assign a time zone
If by your context you knew of a time zone rather than a mere offset, use a ZoneId to instantiate a ZonedDateTime object. A time zone is an offset plus a set of rules for handling anomalies such as Daylight Saving Time (DST).
Specify a proper time zone name in the format of continent/region. 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( "America/Puerto_Rico" );
ZonedDateTime zdt = ldt.atZone( z );
Different time zones
Your question is not clear near the end, about changing offsets. If your goal is to view the date-time through the various lenses of various time zones, you can easily adjust by creating new ZonedDateTime objects. Assign a different time zone to each.
Note that all these date-time objects (zdt, zKolkata, and zAuckland) represent the same moment, the same point on the timeline. Each presents a different wall-clock time but for the same simultaneous moment.
ZoneId zKolkata = ZoneId.of ( "Asia/Kolkata" );
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant ( zKolkata );
ZoneId zAuckland = ZoneId.of ( "Pacific/Auckland" );
ZonedDateTime zdtAuckland = zdt.withZoneSameInstant ( zAuckland );
System.out.println ( "input: " + input + " | ldt: " + ldt + " | odt: " + odt + " | zdt: " + zdt + " | zdtKolkata " + zdtKolkata + " | zdtAuckland: " + zdtAuckland );
Dump to console.
input: 15Sep20162040 | ldt: 2016-09-15T20:40 | odt: 2016-09-15T20:40-04:00 | zdt: 2016-09-15T20:40-04:00[America/Puerto_Rico] | zdtKolkata 2016-09-16T06:10+05:30[Asia/Kolkata] | zdtAuckland: 2016-09-16T12:40+12:00[Pacific/Auckland]
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.
Related
I am trying to parse a timestamp I received from database, I have tried multiple parsing string, but every each of them did not work. I am trying to extract the date and clock.
import java.util.Locale;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class Main{
public static void main(String []args){
try {
// error here!
SimpleDateFormat postgre = new SimpleDateFormat("yyyy-MM-ddXHH:mm:ss.ms", Locale.getDefault());
Date d = postgre.parse("2019-08-07T09:51:17.222Z");
System.out.println(new SimpleDateFormat("HH:mm", Locale.getDefault()).format(d));
System.out.println(new SimpleDateFormat("dd MMMMM yyyy", Locale.getDefault()).format(d));
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
yes, I need to use legacy class.
tl;dr
Simple one-liner using the modern java.time classes that years ago supplanted the terrible legacy classes such as Date and SimpleDateFormat.
java.time.Instant
.parse(
"2019-08-07T09:51:17.222Z"
)
.atOffset(
ZoneOffset.UTC
)
.toLocalDate()
For time-of-day portion, call toLocalTime().
To see the same moment through the wall-clock time used by the people of a particular region (a time zone), apply ZoneId to get a ZonedDateTime object. Then call toLocalDate and LocalTime.
Details
Parse using modern class, Java.time.Instant.
Your input string is in standard ISO 8601 format. The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.
Instant instant = Instant.parse( "2019-08-07T09:51:17.222Z" ) ;
Generate an ISO 8601 string.
String output = instant.toString() ;
To write to database, convert to sibling class OffsetDateTime. While support for Instant is optional in JDBC 4.2 and later, your JDBC driver is required to support OffsetDateTime.
OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;
myPreparedStatement.setObject( … , odt ) ;
Retrieval.
OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;
Instant instant = odt.toInstant() ;
If you want to see the date and the time-of-day for that moment as seen in UTC (as opposed to some time zone), extract a LocalDate and LocalTime.
LocalDate ld = odt.toLocalDate() ;
LocalTime lt = odt.toLocalTime() ;
Best to avoid the terrible legacy class java.util.Date. But if you must, you can convert back and forth using new to/from methods added to the old classes.
java.util.Date d = Date.from( instant ) ;
Likewise, when receiving a Date, immediately convert to an Instant. Then proceed with your business logic.
Instant instant = myDate.toInstant() ;
The correct pattern is "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'". I forgot to use the capital S for milliseconds.
I have a java component to format the date that I retrieve. Here is my code:
Format formatter = new SimpleDateFormat("yyyyMMdd");
String s = "2019-04-23 06:57:00.0";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss.S");
try
{
Date date = simpleDateFormat.parse(s);
System.out.println("Formatter: "+formatter.format(date));
}
catch (ParseException ex)
{
System.out.println("Exception "+ex);
}
The code works great as long as the String s has the format "2019-04-23 06:57:00.0";
My Question is, how to tweak this code so it will work for below scenarios ex,
my s string may have values like
String s = "2019-04-23 06:57:00.0";
or
String s = "2019-04-23 06:57:00";
Or
String s = "2019-04-23";
right now it fails if I don't pass the ms.. Thanks!
Different types
String s = "2019-04-23 06:57:00";
String s = "2019-04-23";
These are two different kinds of information. One is a date with time-of-day, the other is simply a date. So you should be parsing each as different types of objects.
LocalDateTime.parse
To comply with the ISO 8601 standard format used by default in the LocalDateTime class, replace the SPACE in the middle with a T. I suggest you educate the publisher of your data about using only ISO 8601 formats when exchanging date-time values as text.
LocalDateTime ldt1 = LocalDateTime.parse( "2019-04-23 06:57:00".replace( " " , "T" ) ) ;
The fractional second parses by default as well.
LocalDateTime ldt2 = LocalDateTime.parse( "2019-04-23 06:57:00.0".replace( " " , "T" ) ) ;
See this code run live at IdeOne.com.
ldt1.toString(): 2019-04-23T06:57
ldt2.toString(): 2019-04-23T06:57
LocalDate.parse
Your date-only input already complies with ISO 8601.
LocalDate ld = LocalDate.parse( "2019-04-23" ) ;
See this code run live at IdeOne.com.
ld.toString(): 2019-04-23
Date with time-of-day
You can strip out the time-of-day from the date.
LocalDate ld = ldt.toLocalDate() ;
And you can add it back in.
LocalTime lt = LocalTime.parse( "06:57:00" ) ;
LocalDateTime ldt = ld.with( lt ) ;
Moment
However, be aware that a LocalDateTime does not represent a moment, is not a point on the timeline. Lacking the context of a time zone or offset-from-UTC, a LocalDateTime cannot hold a moment, as explained in its class JavaDoc.
For a moment, use the ZonedDateTime, OffsetDateTime, or Instant classes. Teach the publisher of your data to include the offset, preferably in UTC.
Avoid legacy date-time classes
The old classes SimpleDateFormat, Date, and Calendar are terrible, riddled with poor design choices, written by people not skilled in date-time handling. These were supplanted years ago by the modern java.time classes defined in JSR 310.
In case of you have optional parts in pattern you can use [ and ].
For example
public static Instant toInstant(final String timeStr){
final DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH[:mm[:ss[ SSSSSSSS]]]")
.withZone(ZoneId.of("UTC"));
try {
return Instant.from(formatter.parse(timeStr));
}catch (DateTimeException e){
final DateTimeFormatter formatter2 = DateTimeFormatter
.ofPattern("yyyy-MM-dd")
.withZone(ZoneId.of("UTC"));
return LocalDate.parse(timeStr, formatter2).atStartOfDay().atZone(ZoneId.of("UTC")).toInstant();
}
}
cover
yyyy-MM-dd
yyyy-MM-dd HH
yyyy-MM-dd HH:mm
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss SSSSSSSS
I have this code to add 1 hour or 1 day in date Java 8, but doesn´t work
String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
java.text.SimpleDateFormat format = new java.text.SimpleDateFormat(DATE_FORMAT);
Date parse = format.parse("2017-01-01 13:00:00");
LocalDateTime ldt = LocalDateTime.ofInstant(parse.toInstant(), ZoneId.systemDefault());
ldt.plusHours(1);
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date te = Date.from(zdt.toInstant());
What´s wrong? The code shows: Sun Jan 01 13:00:00 BRST 2017
LocalDateTime is immutable and returns a new LocalDateTime when you call methods on it.
So you must call
ldt = ldt.plusHours(1);
Apart from the issue that you don't use the result of your date manipulation (ldt = ldt.plusHours(1)), you don't really need to go via a LocalDateTime for this operation.
I would simply use an OffsetDateTime since you don't care about time zones:
OffsetDateTime odt = parse.toInstant().atOffset(ZoneOffset.UTC);
odt = odt.plusDays(1).plusHours(1);
Date te = Date.from(odt.toInstant());
You could even stick to using Instants:
Instant input = parse.toInstant();
Date te = Date.from(input.plus(1, DAYS).plus(1, HOURS));
(with an import static java.time.temporal.ChronoUnit.*;)
tl;dr
LocalDateTime.parse( // Parse input string that lacks any indication of offset-from-UTC or time zone.
"2017-01-01 13:00:00".replace( " " , "T" ) // Convert to ISO 8601 standard format.
).atZone( // Assign a time zone to render a meaningful ZonedDateTime object, an actual point on the timeline.
ZoneId.systemDefault() // The Question uses default time zone. Beware that default can change at any moment during runtime. Better to specify an expected/desired time zone generally.
).plus(
Duration.ofDays( 1L ).plusHours( 1L ) // Add a span of time.
)
Details
Do not mix the troublesome old legacy classes Date and Calendar with the modern java.time classes. Use only java.time, avoiding the legacy classes.
The java.time classes use the ISO 8601 standard formats by default when parsing and generating strings. Convert your input string by replacing the SPACE in the middle with a T.
String input = "2017-01-01 13:00:00".replace( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse( input ) ;
ALocalDateTime does not represent an actual moment, not a point on the timeline. It has no real meaning until you assign a time zone.
ZoneId z = ZoneId.systemDefault() ; // I recommend specifying the desired/expected zone rather than relying on current default.
ZonedDateTime zdt = ldt.atZone( z ) ;
A Duration represents a span of time not attached to the timeline.
Duration d = Duration.ofDays( 1L ).plusHours( 1L ) ;
ZonedDateTime zdtLater = zdt.plus( d ) ;
I try to convert a string into date type. This is the whole what I did.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHMMMDD");
String date = "23Mar25";
System.out.println(date);
LocalDate localDate = LocalDate.parse(date, formatter);
System.out.println(localDate);
And I got the error information as
Text '23Mar25' could not be parsed:
Unable to obtain LocalDate from TemporalAccessor:
{MonthOfYear=3, DayOfYear=25},ISO resolved to 23:00 of type
java.time.format.Parsed at
java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
Anyone can help me to solve it?
tl;dr
LocalTime.parse( "23Mar25" , DateTimeFormatter.ofPattern ( "HHMMMdd", Locale.US )
.toString() // 23:00
…and…
MonthDay.parse( "23Mar25" , DateTimeFormatter.ofPattern ( "HHMMMdd", Locale.US )
.toString() // --03-25
Details
If you are certain that input indeed represents an hour-of-day, month, and day-of-month…
DateTimeFormatter
Define a single DateTimeFormatter. Be sure to specify a Locale for the human language by which to translate the abbreviated name of the month.
String input = "23Mar25";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "HHMMMdd", Locale.US );
LocalTime
Use that formatter twice. Once to produce a time-of-day as a LocalTime object to hold the hour-of-day.
LocalTime lt = LocalTime.parse ( input, f );
MonthDay
Again to produce a MonthDay object to hold the month and the day-of-month.
MonthDay md = MonthDay.parse ( input , f );
ZoneId
To make a specific moment of that value, we must assign a year and a time zone. If we want to use use the current year, we need a time zone for that as well. Keep in mind the fact that for any given moment both the date and the time-of-day vary around the globe by zone.
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 ( "America/Montreal" );
LocalDate
Determine a LocalDate object by assigning a year to our MonthDay object.
int yearNumber = Year.now ( z )
.getValue ( ) ;
LocalDate ld = md.atYear ( yearNumber );
ZonedDateTime
Combine with the LocalTime to get an exact moment, a point on the timeline. Result is a ZonedDateTime object.
ZonedDateTime zdt = ZonedDateTime.of ( ld, lt, z );
Dump to console.
System.out.println ( "input: " + input );
System.out.println ( "lt: " + lt );
System.out.println ( "md: " + md );
System.out.println ( "ld: " + ld );
System.out.println ( "zdt: " + zdt );
input: 23Mar25
lt: 23:00
md: --03-25
ld: 2017-03-25
zdt: 2017-03-25T23:00-04:00[America/Montreal]
OffsetDateTime
If your input is intended for UTC, then instead of ZoneId and ZonedDateTime, use ZoneOffset.UTC constant and OffsetDateTime class in code similar to above.
OffsetDateTime zdt = OffsetDateTime.of ( ld, lt, ZoneOffset.UTC );
ISO 8601
Notice the output, the strings generated by the toString methods. The java.time classes use the ISO 8601 standard formats by default when parsing/generating strings. I strongly suggest you and your data-source use these standard formats rather than invent your own such as seen in the Question. The java.time classes can directly parse as well as generate such standard strings without needing to specify a formatting pattern.
To generate strings in other formats, use the DateTimeFormatter class. That topic is covered by many other Stack Overflow pages, so search for many examples and more discussion.
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HHMMMDD");
It is using a pattern for:
HH: 2 spaces for hour
MMM: 3 spaces for month
DD: 2 spaces for day
I think you want a format like: `ddmmmyy
dd: 2 spaces for day
mmm: 3 spaces for month
yy: 2 spaces for year
I want to compare the date part of two java.util.Date objects. How can I achieve this? I am not looking to comparing the date, month and year separately.
Thanks in advance!
The commons-lang DateUtils provide a nice solution for this problem:
watch this
With this you can compare two Date instances in a single line, loosing sight of every part of the Date you want.
Date date1 = new Date(2011, 8, 30, 13, 42, 15);
Date date2 = new Date(2011, 8, 30, 15, 23, 46);
int compareTo = DateUtils.truncatedCompareTo(date1, date2,
Calendar.DAY_OF_MONTH);
In this example the value of compareTo is 0.
A Date is just an instant in time. It only really "means" anything in terms of a date when you apply a calendar and time zone to it. As such, you should really be looking at Calendar, if you want to stick within the standard Java API - you can create a Calendar object with the right Date and time zone, then set the time components to 0.
However, it would be nicer to use Joda Time to start with, and its LocalDate type, which more accurately reflects what you're interested in.
Use Calendar.set() with Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND and Calendar.MILLISECOND to set all those fields to 0.
The answers to this duplicate question will be useful: Resetting the time part of a timestamp in Java
Find below a solution which employs Joda Time and supports time zones.
So, you will obtain date and time (into currentDate and currentTime) in the currently configured timezone in the JVM.
Please notice that Joda Time does not support leap seconds. So, you can be some 26 or 27 seconds off the true value. This probably will only be solved in the next 50 years, when the accumulated error will be closer to 1 min and people will start to care about it.
See also: https://en.wikipedia.org/wiki/Leap_second
/**
* This class splits the current date/time (now!) and an informed date/time into their components:
* <lu>
* <li>schedulable: if the informed date/time is in the present (now!) or in future.</li>
* <li>informedDate: the date (only) part of the informed date/time</li>
* <li>informedTime: the time (only) part of the informed date/time</li>
* <li>currentDate: the date (only) part of the current date/time (now!)</li>
* <li>currentTime: the time (only) part of the current date/time (now!)</li>
* </lu>
*/
public class ScheduleDateTime {
public final boolean schedulable;
public final long millis;
public final java.util.Date informedDate;
public final java.util.Date informedTime;
public final java.util.Date currentDate;
public final java.util.Date currentTime;
public ScheduleDateTime(long millis) {
final long now = System.currentTimeMillis();
this.schedulable = (millis > -1L) && (millis >= now);
final TimeZoneUtils tz = new TimeZoneUtils();
final java.util.Date dmillis = new java.util.Date( (millis > -1L) ? millis : now );
final java.time.ZonedDateTime zdtmillis = java.time.ZonedDateTime.ofInstant(dmillis.toInstant(), java.time.ZoneId.systemDefault());
final java.util.Date zdmillis = java.util.Date.from(tz.tzdate(zdtmillis));
final java.util.Date ztmillis = new java.util.Date(tz.tztime(zdtmillis));
final java.util.Date dnow = new java.util.Date(now);
final java.time.ZonedDateTime zdtnow = java.time.ZonedDateTime.ofInstant(dnow.toInstant(), java.time.ZoneId.systemDefault());
final java.util.Date zdnow = java.util.Date.from(tz.tzdate(zdtnow));
final java.util.Date ztnow = new java.util.Date(tz.tztime(zdtnow));
this.millis = millis;
this.informedDate = zdmillis;
this.informedTime = ztmillis;
this.currentDate = zdnow;
this.currentTime = ztnow;
}
}
public class TimeZoneUtils {
public java.time.Instant tzdate() {
final java.time.ZonedDateTime zdtime = java.time.ZonedDateTime.now();
return tzdate(zdtime);
}
public java.time.Instant tzdate(java.time.ZonedDateTime zdtime) {
final java.time.ZonedDateTime zddate = zdtime.truncatedTo(java.time.temporal.ChronoUnit.DAYS);
final java.time.Instant instant = zddate.toInstant();
return instant;
}
public long tztime() {
final java.time.ZonedDateTime zdtime = java.time.ZonedDateTime.now();
return tztime(zdtime);
}
public long tztime(java.time.ZonedDateTime zdtime) {
final java.time.ZonedDateTime zddate = zdtime.truncatedTo(java.time.temporal.ChronoUnit.DAYS);
final long millis = zddate.until(zdtime, java.time.temporal.ChronoUnit.MILLIS);
return millis;
}
}
tl;dr
LocalDate ld =
myUtilDate.toInstant()
.atZone( ZoneId.of( "America/Montreal" ) )
.toLocalDate() ;
Avoid legacy date-time classes
You are using troublesome old legacy date-time classes. Avoid them.
Instead use java.time classes. These supplant the old classes as well as the Joda-Time library.
Convert
Convert your java.util.Date to an Instant.
The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = myUtilDate.toInstant();
Time Zone
Apply a time zone. Time zone is crucial. For any given moment the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while also being “yesterday” in Montréal Québec.
Apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
Local… types
The LocalDate class represents a date-only value without time-of-day and without time zone. Likewise, the LocalTime represents a time-of-day without a date and without a time zone. You can think of these as two components which along with a ZoneId make up a ZonedDateTime. You can extract these from a ZonedDateTime.
LocalDate ld = zdt.toLocalDate();
LocalTime lt = zdt.toLocalTime();
Strings
If your goal is merely generating Strings for presentation to the user, no need for the Local… types. Instead, use DateTimeFormatter to generate strings representing only the date-portion or the time-portion. That class is smart enough to automatically localize while generating the String.
Specify a Locale to determine (a) the human language used for translating name of day, name of month, and such, and (b) the cultural norms for deciding issues such as abbreviation, capitalization, punctuation, and such.
Locale l = Locale.CANADA_FRENCH ; // Or Locale.US, Locale.ITALY, etc.
DateTimeFormatter fDate = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( locale );
String outputDate = zdt.format( fDate );
DateTimeFormatter fTime = DateTimeFormatter.ofLocalizedTime( FormatStyle.MEDIUM ).withLocale( locale );
String outputTime = zdt.format( fTime );
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome 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.