Java LocalDateTime remove the milliseconds from UTC timezone - java

I am trying to truncate milliseconds from a UTC time zone.
I have the code below where I am able to remove milliseconds but I still get the Z at the end.
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC );
OffsetDateTime eventDateTime=now.minus(4, ChronoUnit.MINUTES);
System.out.println("====Event date Time before truncate===");
System.out.println(eventDateTime);
System.out.println("====Event date Time after truncate===");
System.out.println(eventDateTime.truncatedTo(ChronoUnit.SECONDS));
This outputs the following:
====Event date Time before truncate===
2021-03-09T20:46:24.081Z
====Event date Time after truncate===
2021-03-09T20:46:24Z

tl;dr
To represent a moment in UTC, use Instant class rather than LocalDateTime or OffsetDateTime.
Instant
.now() // Returns a `Instant` representing the current moment including a fractional second.
.truncatedTo(
ChronoUnit.SECONDS // Granularity of what you want chopped off. In this case, we lop off any fractional second.
) // Returns another `Instant` object. Per immutable objects, a new object is instantiated rather than altering ("mutating") the original.
.minus( // Go backwards on the timeline.
Duration.ofMinutes( 4 ) // Amount of time to go backwards.
) // Returns another `Instant` object.
.toString() // Generate text in standard ISO 8601 format, with `Z` on the end meaning an offset of zero hours-minutes-seconds from the UTC temporal prime meridian.
Zulu time
The Z at the end represents vital information: the date-time represents a moment as seen with an offset from UTC of zero hours-minutes-seconds. This letter is defined as part of the standard formats in ISO 8601. The letter is pronounced “Zulu” per aviation/military tradition.
If you do not care about offset or time zone in your problem domain, then you are using the wrong type.
The types Instant, OffsetDateTime, and ZonedDateTime all represent a moment, a specific point on the timeline. All three involve an offset or time zone.
LocalDateTime
If you want only a date with time-of-day but lacking the context of an offset/zone, then use LocalDateTime. No Z will appear in text representing the value of a LocalDateTime because no offset is involved.
Just be aware that a LocalDateTime object is inherently ambiguous, and does not represent a moment. In other words, calling LocalDateTime.now is almost never the right thing to do. But if you insist:
LocalDateTime ldt = LocalDateTime.now( ZoneOffset.UTC ) ;

The Z is the timezone information. You can convert the OffsetDateTime instance to a LocalDateTime like this:
eventDateTime.truncatedTo(ChronoUnit.SECONDS).toLocalDateTime()

There can be two ways:
Preferred way: Use DateTimeFormatter
Use OffsetDateTime#toLocalDateTime: The problem with this approach is that the output string is automatically truncated to minutes if the seconds part is zero.
Demo:
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
DateTimeFormatter dtfWithoutSecFraction = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss");
DateTimeFormatter dtfWithSecFraction = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSSSS");
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
OffsetDateTime eventDateTime = now.minusMinutes(4);
System.out.println("====Event date Time before truncate===");
System.out.println(eventDateTime);
System.out.println(eventDateTime.toLocalDateTime());
System.out.println(dtfWithSecFraction.format(eventDateTime));
System.out.println("====Event date Time after truncate===");
System.out.println(eventDateTime.truncatedTo(ChronoUnit.SECONDS));
System.out.println(eventDateTime.truncatedTo(ChronoUnit.SECONDS).toLocalDateTime());
System.out.println(dtfWithoutSecFraction.format(eventDateTime.truncatedTo(ChronoUnit.SECONDS)));
}
}
Output:
====Event date Time before truncate===
2021-03-09T21:17:17.589016Z
2021-03-09T21:17:17.589016
2021-03-09T21:17:17.589016
====Event date Time after truncate===
2021-03-09T21:17:17Z
2021-03-09T21:17:17
2021-03-09T21:17:17
Note that Z stands for Zulu which specifies date-time in UTC (i.e. a timezone offset of +00:00 hours).
The following table gives you an overview of java.time types:
As you can see, OffsetDateTime, ZonedDateTime etc. carry timezone information and you can get just the date-time part using the techniques mentioned in the solution above.
Learn more about the modern date-time API from Trail: Date Time.

Related

Java - convert timestamp to Date and back to timestamp changes the date

I'm creating a string out from current time and I wanted to convert it to timestamp again, but the thing is, that it's subtracts 2 hours while converting.
This is the steps I'm doing -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
output -
1639065502667
1639058302000
(2021-12-09 15:58:22
2021-12-09 13:58:22)
why is the diff of the 2 hours?
how can I parse it so that the outputs will be equal?
tl;dr
Trying to understand Date, Calendar, and SimpleDateFormat is a huge waste of time. Use only their replacements, the java.time classes.
LocalDateTime // Represent a date with time-of-day, but lacking the context of a time zone or offset-from-UTC. So *not* a moment, *not* a point on the timeline.
.parse( // Parse your text string input into a date-time object.
"2021-12-09 15:58:22" // Your input of date with time-of-day but no offset/zone.
.replace( " " , "T" ) // Replace SPACE with a `T` to comply with standard ISO 8601 format.
) // Returns a `LocalDateTime` object.
.atZone( // Place that date-with-time into the context a particular time zone.
ZoneId.of( "America/Montreal" ) // Specify a time zone by its `Continent/Region` name.
) // Returns a `ZonedDateTime` object, a date with time-of-day as seen through the wall-clock time used by the people of a particular region. This *does* represent a moment, *is* a specific point on the timeline.
.toInstant() // Adjust from time zone to UTC (an offset of zero hours-minutes-seconds). This represents the very same moment as the `ZonedDateTime` object above, but as seen through a different wall-clock time.
.toEpochMilli() // Get a count of milliseconds from first moment of 1970 in UTC (1970-01-01T00:00Z) to the moment of our `Instant` object (& `ZonedDateTime` object).
See this code run live at IdeOne.com. There you can click fork to make a copy, alter, and run.
1639083502000
Avoid legacy date-time classes
Regarding your specific question about a two hour difference, the obvious cause would be a time zone difference.
Parsing incomplete information
Your parsing, SimpleDateFormat("yyyy-MM-dd HH:mm:ss") is something of a wild card. The result will be a java.util.Date object, which represents a moment, a date with time-of-day as seen with an offset of zero. But your input lacks an indicator of offset or zone. As commented by Sotirios Delimanolis, you are parsing with partial input, with incomplete information.
So some default zone/offset will be applied. I do not know what zone or offset in particular, because I do not care. That terrible class is tragically flawed, and should be avoided.
Also, yet another problem with the Date class is that its toString method has the anti-feature of applying the JVM’s current default time zone to adjust away from the UTC value represented by that class. Very confusing, as this creates the illusion of that zone being a part of Date object but it is not. As I said, a terrible class, tragically flawed.
Use only java.time classes instead.
java.time
Understand that a date with time-of-day is inherently ambiguous, is not a moment.
If you are tracking 4 PM on the 9th, we do not know if that means 4 PM in Tokyo Japan, 4 PM in Toulouse France, or 4 PM in Toledo Ohio US — three very different moments that happen several hours apart.
LocalDateTime ldt = LocalDateTime.parse( "2021-12-09 16:00:00" ) ;
To track a moment, a point on the timeline, you must place ne date-with-time in the context of an offset from UTC or of a time zone.
An offset is merely a number of hours-minutes-seconds ahead or behind the baseline of modern timekeeping, the prime meridian at Royal Observatory, Greenwich.
A time zone is much more. A time zone is a named history of the past, present, and future changes to the offset used by the people of a particular region. Each zone has a name in format of Continent/Region such as Europe/Berlin or Asia/Tokyo.
To track moments as seen in UTC, with an offset of zero, use Instant.
Instant instant = Instant.now() ;
To see that same moment through the wall-clock time used by people in a region, apply a ZoneId to get a ZonedDateTime.
ZoneId z = ZoneId.of( "America/Edmonton" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
As for your use of SimpleDateFormat, Date, and Calendar, don’t. Avoid these legacy date-time classes. Hey were designed by people who did not understand date-time handling. They were supplanted years ago by the modern java.time classes defined in JSR 310. Sun, Oracle, and the JCP community all gave up on those classes. I suggest you do the same.
In your code:
long ts = Clock.systemUTC().millis();
Instant instant = Instant.ofEpochMilli(ts);
That is the same as doing this:
Instant.now().truncatedTo( ChronoUnit.MILLISECONDS )
In your code:
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
(A) When working with mere offsets rather than time zones, use OffsetDateTime class. The ZonedDateTime class is for working with time zones.
(B) A briefer way to adjust from Instant to a zoned moment was shown above:
myInstant.atZone( z )
The answer was only setting the timezone to UTC -
DateTimeFormatterBuilder dateTimeFormatterBuilder = new DateTimeFormatterBuilder().append(DateTimeFormatter.ofPattern("uuuu-MM-dd")).appendLiteral(" ")
.append(DateTimeFormatter.ofPattern("HH:mm:ss")).parseLenient();
long ts = Clock.systemUTC().millis();
System.out.println(ts);
Instant instant = Instant.ofEpochMilli(ts);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(instant, ZoneOffset.UTC);
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
*******
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
*******
long timestamp = simpleDateFormat.parse(str).getTime();
System.out.println(timestamp);
} catch (ParseException e) {
e.printStackTrace();
}
The dateTimeFomatter builder uses format without milliseconds and without timezone.
That's why the str value contain no information about them.
Then simpleDateFormat.parse(str) uses timezone of JVM which is UTC+02:00 in this case.
Trace what is going on:
Instant instant = Instant.now();
// => 2021-12-09 15:58:22.798 +00:00
String str = zonedDateTime.format(dateTimeFormatterBuilder.toFormatter());
// => "2021-12-09 15:58:22"
simpleDateFormat.parse(str);
// => 2021-12-09 15:58:22.000 +02:00
You just need to fix the pattern (add millis .SSS and timezone XXX parts) to make the results consistent as expected:
DateTimeFormatter.ofPattern("HH:mm:ss.SSSXXX")
// and something similar for SimpleDateFormat if needed
Parsing Instant from a custom formatted string.
This example shows how to parse Instant from serialized time assuming that there is a fixed timezone for all cases.
var serializedDateTime = "2020-01-01 10:20:30";
var zoneId = ZoneOffset.UTC; // may be any other zone
var format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
var instant = LocalDateTime
.parse(serializedDateTime, format)
// LocalDateTime is unzoned, just representation of (date + time) numbers in a single object
.atZone(zoneId)
// apply a zone to get ZonedDateTime (point in space-time on Earth)
.toInstant();
// convert to Instant (the same point adjusted to UTC+00:00)
Let me guess, your timezone is UTC+2?
simpleDateFormat.parse(str) assume that your date in current system timezone, but it is in UTC.

Date in string to LocalDateTime conversion in java

DateTimes in different formats is always a problem for me. I have a date with the datatype string like "2021-07-25"
I want to convert this date to the datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00. I have the get and set property like below
private LocalDateTime relationshipStatusDate;
public LocalDateTime getRelationshipStatusDate() {
return relationshipStatusDate;
}
public void setRelationshipStatusDate(LocalDateTime relationshipStatusDate) {
this.relationshipStatusDate = relationshipStatusDate;
}
public void setRelationshipStatusDate(String time) {
if (time != null) {
try {
long epochTime = Long.parseLong(time);
this.relationshipStatusDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochTime), ZoneOffset.UTC);
} catch (NumberFormatException e){
this.relationshipStatusDate = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
}
}
}
and I am trying to format like below and its failing with an error "Unknown pattern letter T"
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-ddT00:00:00.000-05:00");
LocalDateTime dateTime = LocalDateTime.parse(statusDate, formatter);
Your format won't be parsable as it doesn't support default values like T00:00:00.000-05:00. You could escape literals e.g. use 'T00:00...' but that would just make the parser ignore them.
Instead, if all you get is a date then only parse a date and add the default time after that, e.g. like this:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
LocalDate date = LocalDate.parse(statusDate, formatter );
LocalDateTime dateTime = date.atStartOfDay(); //"relative" representation as the absolute instant would require adding a timezone
ZonedDateTime zonedDT = date.atSTartOfDay(ZoneOffset.UTC); //"absolute" representation of instant
I want to convert this date to the datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00.
Note the potential misconception here: LocalDateTime does NOT have a format. It represents a date and time (from a local point of reference - not in absolute terms as the timezone is missing) and provides access to individual fields such as day of month, day of week etc. but it is not formatted. Formatting is applied when you convert that date object to a string.
tl;dr
LocalDate // Represent a date-only value, without a time-of-day and without a time zone or offset-from-UTC.
.parse( "2021-07-25" ) // Parse a string in standard ISO 8601 format to instantiate a `LocalDate` object.
.atStartOfDay( // Determine the first moment of the day on that date in that zone. NB: The day does *not* always begin at 00:00, so never assume that time.
ZoneId.of( "America/Bogota" ) // Real time zones have a name in Continent/Region format. Never use 2-4 letter pseudo-zones such as `CST` or `IST`.
) // Returns a `ZonedDateTime` object, a moment in the context of a time zone.
.toOffsetDateTime() // Strips away the time zone information, leaving only a date with time-of-day in a particular offset. Returns an `OffsetDateTime` object.
.toString() // Generate text in standard ISO 8601 format.
2021-07-25T00:00-05:00
Details
LocalDateTime is the wrong class
You said:
datatype LocalDateTime in the format 2021-07-25T00:00:00.000-05:00
That is a contradiction. The -05:00 at the end of your string is an offset-from-UTC. A LocalDateTime object has no offset.
You seem to misunderstand the purpose of LocalDateTime. That class does not represent a moment as seen through the wall-clock time with an offset-from-UTC used by the people of a particular region. For that purpose, use OffsetDateTime, or preferably, ZonedDateTime.
Use documentation rather than intuition when programming with unfamiliar classes. To quote the Javadoc for LocalDateTime:
A date-time without a time-zone … such as 2007-12-03T10:15:30.
…
This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, … It cannot represent an instant on the time-line …
Here is my chart to summarize the types. Three classes represent a moment, while LocalDateTime does not.
You said:
I have a date with the datatype string like "2021-07-25"
So use LocalDate to represent that value.
By default, the java.time classes use standard ISO 8601 format when parsing/generating text. So no need to specify a formatting pattern here, as your input complies with that standard.
LocalDate ld = LocalDate.parse( "2021-07-25" ) ;
You said:
I want to convert this date to … the format 2021-07-25T00:00:00.000-05:00
Your example there uses only a mere offset rather than a time zone. I suggest you use a time zone whenever possible.
An offset is simply a number of hours-minutes-seconds, nothing more. A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by people of a particular region, as decided by their politicians.
ZoneId
So specify your time zone using Continent/Region naming.
ZoneId z = ZoneId.of( "America/Cancun" );
ZonedDateTime
Let java.time determine the first moment of the day on that date as seen in that time zone. Be aware that the day does not always start at 00:00. So never hard-code that time-of-day; let java.time do the work here.
ZonedDateTime zdt = ld.atStartOfDay( z ); // Determine the first moment of the day as seen in that zone. Not always 00:00.
Generate text to represent the value inside our ZonedDateTime. The ZonedDateTime#toString method generates text in a format that wisely extends the ISO 8601 format by appending the name of the time zone in square brackets.
String output = zdt.toString();
2021-07-25T00:00-05:00[America/Cancun]
Pull all that code together.
LocalDate ld = LocalDate.parse( "2021-07-25" );
ZoneId z = ZoneId.of( "America/Cancun" );
ZonedDateTime zdt = ld.atStartOfDay( z ); // Determine the first moment of the day as seen in that zone. Not always 00:00.
String output = zdt.toString();
See this code run live at IdeOne.com.
2021-07-25T00:00-05:00[America/Cancun]
OffsetDateTime
If you insist on generating text in your stated format, while omitting the name of the time zone, use OffsetDateTime.
String output = zdt.toOffSetDateTime().toString() ;
All of these topics have been covered many times on Stack Overflow. Search to learn more.

Difficulty formatting dates in Groovy

I am having some issues formatting dates in Groovy. I am trying to convert a string back to a localdate and its not taking it so well....
DateTimeFormatter formatDates = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm");
LocalDate currentLocalDate = LocalDate.now();
// modify the local date to the previous day
LocalDate previousDateLocalDate = currentLocalDate.minusDays(1)
// cast localdates to strings
String startDateString = previousDateLocalDate.toString() + " 00:00"
String endDateString = previousDateLocalDate.toString() + " 23:59"
// cast strings to localdates
LocalDate startDateLocalDate = LocalDate.parse(startDateString, formatDates);
The output is only showing what was in the previousDateLocalDate variable :
2019-03-06
I am not sure why its dropping the hh:mm. Could it be my format or is my syntax wrong. Any ideas would be greatly appreciated. Is it possible when I subtract a day off from my current day to just format it how I need it to be there instead or set the format when I create the LocalDate.now()?
-Thanks
Edit 1: Let me also add that the minusDays may vary so there might be a better way to get the previous day before yesterday but in some cases it might be 7, 11, etc...
Specify time zone explicitly
You should always specify explicitly the desired/expected time zone when calling now. For any given moment, the date varies around the globe by time zone. It might be “tomorrow” in Tokyo Japan while “yesterday” in Casablanca Morocco. When you omit the zone, the JVM’s current default zone is implicitly applied at runtime – so your results may vary.
ZoneId z = ZoneId.of( "Africa/Casablanca" ) ; // Or `ZoneId.systemDefault` if you actually want the JVM’s current default time zone.
LocalDate ld = LocalDate.now( z ) ; // Capture the current date as seen in the wall-clock time used by the people of a particular region (a time zone).
LocalDate
LocalDate class represents only a date, without time-of-day and without time zone or offset-from-UTC.
If you wish to combine a time-of-day with a date, use one of the other classes.
Date-time math
The java.time classes offer plus… and minus… methods for adding or subtracting a span of time.
LocalDate yesterday = ld.minusDays( 1 ) ;
First moment of the day
Apparently you want the first moment of a date. A couple things to understand here. Firstly, a time zone is needed. As discussed above, a new day dawns at different moments around the globe by zone. Secondly, do not assume the day starts at 00:00:00. Anomalies such as Daylight Saving Time (DST) means the day on some dates in same zones may start at another time, such as 01:00:00. Let java.time determine the first moment.
ZonedDateTime zdt = ld.atStartOfDay( z ) ; // Let java.time determine the first moment of the day.
Half-Open
Apparently you want the end of day. Tracking the last moment of the day is problematic. For example, your 23:59 text will miss any moment of that last minute of the day.
Generally, a better approach to tracking spans of time is the Half-Open approach where the beginning is inclusive while the ending is exclusive. So a day starts with the first moment of the day and runs up to, but does not include, the first moment of the next day.
ZonedDateTime start = ld.atStartOfDay( z ) ; // Start of today.
ZonedDateTime stop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Start of tomorrow.
DateTimeFormatter
To generate strings representing your date-time object’s value, use a DateTimeFormatter object. I’ll not cover that here, as it has been covered many many many times already on Stack Overflow.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm" ) ;
String output = zdt.format( f ) ; // Generate text representing the value of this `ZonedDateTime` object.
Keep in mind that date-time objects do not have a “format”, only a textual representation of a date-time object’s value has a format. Do not conflate the string object with the date-time object. A date-time object can parse a string, and can generate a string, but is not itself a string.
try this tool
import grails.gorm.transactions.Transactional
import org.springframework.stereotype.Component
import java.time.LocalDate
import java.time.Period
import java.time.ZoneId
import java.time.chrono.ChronoLocalDate
import java.time.chrono.Chronology
import java.time.chrono.MinguoChronology
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.format.DecimalStyle
import java.time.temporal.TemporalAccessor
import java.time.temporal.TemporalAdjusters
Date mgStringToDate(String mgString, String separator = "/") {
if(mgString){
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
Chronology chrono = MinguoChronology.INSTANCE;
DateTimeFormatter df = new DateTimeFormatterBuilder().parseLenient()
.appendPattern("yyy${separator}MM${separator}dd").toFormatter().withChronology(chrono)
.withDecimalStyle(DecimalStyle.of(locale));
TemporalAccessor temporal = df.parse(mgString);
ChronoLocalDate cDate = chrono.date(temporal);
Date date = Date.from(LocalDate.from(cDate).atStartOfDay(ZoneId.systemDefault()).toInstant());
return date
}else{
return null
}
}

Convert elapsed time between epoch time and current moment to ISO 8601 duration with Java

I have milliseconds since 1970 january 1 UTC (Epoch time).
1512431637067
I need to convert this to something like (ISO-8601 duration). This will be based off current today's date.
P5M4D
Any idea how to do this in a simple way using java code?
ZoneId zone = ZoneId.of("Europe/Guernsey"); // Specify a time zone by proper name `Contintent/Region`, never by 3-4 letter codes such as `PST`, `CST`, or `IST`.
LocalDate then = // Represent a date-only value, without time zone and without time-of-day.
Instant.ofEpochMilli(1_512_431_637_067L) // Parse your number of milliseconds since 1970-01-01T00:00Z as a value in UTC.
.atZone(zone) // Adjust from UTC to some other zone. Same moment, different wall-clock time. Returns a `ZonedDateTime`.
.toLocalDate(); // Extract a date-only value.
LocalDate today = LocalDate.now(zone); // Get the current date as seen in the wall-clock time in use by the people of a particular region.
Period diff = Period.between(then, today); // Determine the number of years-months-days elapsed.
System.out.println(diff); // Generate a String is standard ISO 8601 format: `PnYnMnDTnHnMnS`.
Output when running just now is exactly what you asked for:
P5M4D
The result is time zone dependent. For any given moment, the date varies around the globe by zone.
Therefore please substitute your desired time zone if it didn’t happen to be Europe/Guernsey. Use ZoneOffset.UTC and OffsetDateTime class if you want the calculation to happen in UTC.
For example running that code above for Europe/Guernsey results in P5M4D, while switching to Europe/Moscow results in P5M3D, for a difference of one day depending on which zone you specify.
Period.between(then, LocalDate.now(ZoneId.of("Europe/Moscow")))
Output on the day the question was asked would have been:
P5M3D
For a duration that includes units larger than a day you need to use the Period class of java.time The Duration class is for smaller units, days-hours-minutes-seconds-nanoseconds.
Strictly spoken, you cannot because a so called "epoch time" is really an instant and NOT a duration. But you might want to model the elapsed time since that epoch (Unix epoch) as duration. So here you go:
System.out.println(Duration.of(1512431637067L, ChronoUnit.MILLIS));
// output: PT420119H53M57.067S
The method java.time.Duration.toString() automatically normalizes the seconds and nanoseconds to HMS-format (otherwise we have to state that the printing capabilities of the new duration class are limited). If you wish more control over ISO-format then either consider your own workaround using methods like toHours() etc. or use a 3rd-party-library for duration printing.
Another thing: 1512431637067 seems to be in milliseconds not in seconds as you stated otherwise you would get an instant in far future:
System.out.println(Instant.ofEpochMilli(1512431637067L));
// output: 2017-12-04T23:53:57.067Z
System.out.println(Instant.ofEpochSecond(1512431637067L));
// far future: +49897-01-18T19:11:07Z
try this line
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.Locale;
public class HelloWorld
{
public static void main(String[] args)
{
Date date=new Date (1512431637067L);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.US);
System.out.print(df.format(date));
}
}
output date on iso 8601:
2017-12-04T23:53:57.067Z

How can i convert time zone string to the TimeZone Object in java?

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”.

Categories

Resources