Receiving same ZonedDateTime for different time strings - java

I'm using the below function to convert a microsecond format time string to ZoneDateTime so I can do comparisons later.
public static ZonedDateTime createTimeFromString(String inputTime) {
ZonedDateTime time;
try {
System.out.printf("Input time %s%n", inputTime);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyyMMdd-HH:mm:ss.SSSSSS");
LocalDate date = LocalDate.parse(inputTime, formatter);
time = date.atStartOfDay(ZoneId.systemDefault());
System.out.printf("Formated time %s%n", time);
return time;
}
catch (DateTimeParseException exc) {
System.out.printf("%s is not parsable!%n", inputTime);
throw exc; // Rethrow the exception.
}
}
However, whatever time string I pass into the function I get the same output.
eg:
Input time 20171025-10:58:24.062151
Formated time 2017-10-25T00:00+05:30[Asia/Colombo]
Input time 20171025-10:58:25.446862
Formated time 2017-10-25T00:00+05:30[Asia/Colombo]
I'm using Java 8.
Can you please clarify what I'm doing wrong?

When you call LocalDate.parse, you're getting just the date part (day, month and year) and discarding the rest. A LocalDate doesn't have the time fields (hour, minute, seconds and fraction of second), so they are simply discarded and lost.
Then you call atStartOfDay(ZoneId.systemDefault()), which sets the time to midnight at the JVM default timezone.
If you want to keep everything (date and time), parse it to a LocalDateTime, which is a class that contains all the date and time fields. Then you call the atZone method to convert it to a ZonedDateTime:
String inputTime = "20171025-10:58:24.062151";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd-HH:mm:ss.SSSSSS");
// parse to a LocalDateTime (keeping all date and time fields)
LocalDateTime date = LocalDateTime.parse(inputTime, formatter);
// convert to ZonedDateTime
ZonedDateTime z = date.atZone(ZoneId.systemDefault());
PS: ZoneId.systemDefault() returns the JVM default timezone, but keep in mind that this can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.
The API uses IANA timezones names (always in the format Region/City, like Asia/Colombo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like IST or CET) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds(). Then you call the ZoneId.of() method with the zone name, like this:
// using specific timezone instead of JVM's default
ZonedDateTime z = date.atZone(ZoneId.of("Asia/Colombo"));

Related

Why I am not able to parse 2013-03-10T02:59:59Z from PST in Java using parse method of java.text.SimpleDateFormat?

For certain input values though format and all things are correct, I am getting ParseException from SimpleDateFormat.parse(String input) method.
I'm in a scenario where I need to convert time from PST timezone to local timezone; it works almost all of the time for acceptable input except for 2013-03-10T02:00:00Z to 2013-03-10T02:59:59Z.
It seems strange; I tried several JDKs and machines, but the result is the same, it throws
Method threw 'java.text.ParseException' exception.
My expectation is to parse this date properly and return non null date object instead of throwing exception.
Snippet to test this:
public static void main(String[] args) throws Exception {
System.out.println(getDateFromString("2013-03-10T02:59:59Z", "yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH));
}
public static Date getDateFromString(String dateText, String format, Locale locale) {
Date date = null;
SimpleDateFormat simpleDateFormat = null;
try {
simpleDateFormat = new SimpleDateFormat(format, locale);
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
//Supporting strict validation of date format
simpleDateFormat.setLenient(false);
date = simpleDateFormat.parse(dateText);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
tl;dr
Your parse attempt failed because you misinterpreted the input text. The resulting erroneous value represented a date and time in a particular time zone that never existed.
Instead, correctly parse your input, and adjust into a time zone, using java.time classes.
Instant
.parse( "2013-03-10T02:00:00Z" )
.atZone( ZoneId.of( "America/Los_Angeles" ) )
.toString()
See Ideone.com.
2013-03-09T18:00-08:00[America/Los_Angeles]
Your code
Never put quotes around the Z in a date-time formatting pattern. That says to expect but ignore vital information: an offset from UTC of zero hours-minutes-seconds.
You asked:
why am I getting an issue with current code?
As other commented, I assume the legacy date-time classes attempted to parse your input of xx while guessing the value PST meant the time zone America/Los_Angeles. This would have happened because (a) you told the formatter to ignore the actual intended offset of zero represented by that Z, and then you (b) told the formatter to assume a default zone of PST.
So your code defies the intentions of whomever sent you that input data. Instead of 2013-03-10 02:59:59 in UTC, you tried to parse a value of 2013-03-10 02:59:59 in America/Los_Angeles.
As others have commented, that moment never existed. At 2 AM on March 13, 2013 all the people using time zone America/Los_Angeles jumped their clocks forward to 3 AM. This jump is the “Spring Ahead” cutover for Daylight Saving Time (DST). That day of March 13th was only 23 hours long, not 24 hours. The 02:00-03:00 hour was excised.
👉🏽 So your parse attempted failed because your misinterpreted input was coincidentally found to be senseless.
Revised code
Use modern java.time classes. Never use the terrible legacy classes.
Your input is in standard ISO 8601 format. No need to define a formatting pattern.
Instant instant = Instant.parse( "2013-03-10T02:59:059Z" ) ;
instant.toString() = 2013-03-10T02:59:59Z
Adjust to a desired time zone.
PST is not a real time zone. Perhaps you meant the time zone used by much of the west coast of the United States mainland.
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString() = 2013-03-09T18:59:59-08:00[America/Los_Angeles]
Note the change in date as well as time-of-day.
See that code run.
If you must have a java.util.Date to interoperate with code not yet updated to java.time, use new conversion methods added to the old classes.
java.util.Date d = Date.from( instant ) ; // A date-time as seen in UTC, an offset of zero hours-minutes-seconds.
Always search Stack Overflow before posting. All this has been covered many times before. Search to learn more.

Kotlin date parsing is not working properly

So I have been looking up on how to properly parse an incoming datetime, problem is that this string contains the zone as well which apparently can't be parsed for some reason.
To give an example of the incoming date time string:
2021-10-05T10:00:00.0000000
Now I tried to the following:
var dateTimeString = "2021-10-05T10:00:00.0000000"
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
var date = LocalDate.parse(dateTimeString, formatter)
I tried replacing Z with nothing and with ZZZZ, but that didn't work I assume it doesn't work because of the plus or minus sign not being present. FYI, I receive the date in this format because of the Microsoft Graph API when retrieving the calendar events.
Any idea as to how this date should be formatted properly?
Edit: This comes from Microsoft Graph. Basically they give like a date as an object:
"start": {
"dateTime": "2021-10-05T10:00:00.0000000",
"timeZone": "UTC"
}
This is the page of the documentation that explains this date object: dateTimeTimeZone resource type.
Update:
I was finally able to solve this date issue, what I did was the following:
var inputDateTime = "2021-10-05T10:00:00.0000000"
var inputTimeZone = "UTC"
var zonedDateTime = ZonedDateTime.parse(
inputDateTime,
DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.of(inputTimeZone))
).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()
This way, the date would be converted correctly to the right time zone and to the right Date/Time.
As I can see from docs you've provided https://learn.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0
dateTime String A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).
timeZone String Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.
dateTime object has no zone encoded. And all 7 zeroes represent fractions of a second. In such case it's regular ISO_DATE_TIME and you don't need to create your own formatter.
The following code should work
var dateTimeString = "2021-10-05T10:00:00.0000000"
var date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME)
Your Date-Time string does not have timezone information
Your Date-Time string, 2021-10-05T10:00:00.0000000 does not have timezone information. The .0000000 represents the fraction of a second and as of now, java.time is capable of handling up to 9 digits of precision (i.e. nanosecond of precision).
Since it does not have timezone information, it represents a local Date-Time and hence should be parsed into LocalDateTime.
You do not need a DateTimeFormatter for your Date-Time string
The modern Date-Time API is based on ISO 8601 and does not require using a DateTimeFormatter object explicitly as long as the Date-Time string conforms to the ISO 8601 standards. Your Date-Time string is already in the ISO 8601 format.
Demo:
import java.time.LocalDateTime;
public class Main {
public static void main(String args[]) {
var dateTimeString = "2021-10-05T10:00:00.0000000";
var ldt = LocalDateTime.parse(dateTimeString);
System.out.println(ldt);
}
}
Output:
2021-10-05T10:00
ONLINE DEMO
How to get ZonedDateTime out of the LocalDateTime instance?
You can use LocalDateTime#atZone to convert attach a ZoneId to a LocalDateTime resulting into a ZonedDateTime.
ZonedDateTime zdt = ldt.atZone(ZoneId.of("Etc/UTC"));
Note: If you need an Instant, you can get it from this instant of ZonedDateTime e.g.
Instant instant = zdt.toInstant();
An Instant represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator for a zero-timezone offset. It stands for Zulu and specifies the Etc/UTC timezone (which has the timezone offset of +00:00 hours).
Learn more about the modern Date-Time API* from Trail: Date Time.
* If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time.
For the case that the last 4 digits in your example String are not representing a time zone:
Parse it without a formatter (no need since it would be perfect ISO standard if the last 4 digits are just additional fractions of second), but also consider the time zone you get with the JSON:
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
fun main() {
// the time String from JSON
var dateTimeString = "2021-10-05T10:00:00.0000000"
// the zone from JSON (may not always work, but does with UTC)
var timeZone = "UTC"
// create the zone from the timezone String
var zoneId = ZoneId.of(timeZone)
// then parse the time String using plain LocalDateTime and add the zone afterwards
var zdt: ZonedDateTime = LocalDateTime.parse(dateTimeString).atZone(zoneId)
// print some results
println("Full ZonedDateTime: ${zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}")
println("(Local)Date only: ${zdt.toLocalDate()}")
}
Full ZonedDateTime: 2021-10-05T10:00:00Z[UTC]
(Local)Date only: 2021-10-05
Please note that parsing the time zones currently supported by Windows won't work this easy (except from UTC), but the time zones supported by the calendar API are (mostly) sufficient for the creation of a java.time.ZoneId.
As a supplement: Your documentation mentions Pacific Standard Time as a time zone string that may come as part of your MS Graph dateTimeTimeZone. I don’t think the other answers can handle that one, so I should like to show how you do handle it in Java.
private static final DateTimeFormatter ZONE_FORMATTER
= DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);
static ZoneId parseZone(String timeZoneString) {
try {
return ZoneId.from(ZONE_FORMATTER.parse(timeZoneString));
} catch (DateTimeParseException dtpe) {
return ZoneId.of(timeZoneString);
}
}
I believe that this handles the strings mentioned in the documentation and also UTC from your question. I am demonstrating just three different ones:
String[] msTimeZoneStrings = { "UTC", "Pacific Standard Time", "Pacific/Honolulu" };
for (String zoneString : msTimeZoneStrings) {
ZoneId zid = parseZone(zoneString);
System.out.format("%-21s parsed to %s%n", zoneString, zid);
}
Output:
UTC parsed to UTC
Pacific Standard Time parsed to America/Los_Angeles
Pacific/Honolulu parsed to Pacific/Honolulu
In the formatter I use zzzz is for time zone name like Pacific Standard Time. My parse method tries this formatter first. It fails for UTC and Pacific/Honolulu. When the DateTimeParseException is caught, the method next tries the ZoneId.of method also used in the other answers. It handles UTC and all the time zone ID sin region/city format mentioned in the documentation.
Combine the ZoneId obtained from my method with the parsed LocalDateTime to get a ZonedDateTime the way that deHaar is already showing in their answer.

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.

Convert datetime string in one timezone to another using offset

I have a datetime string "2018-01-15 01:16:00" which is in EST timezone. I want to convert this into another timezone dynamically using the UTC offset. My javascript code passes this UTC offset as a parameter and the servlet has to convert/format this datetime string to the timezone identified by the provided offset.
I have tried many approaches including the one documented in the oracle tutorials but unable to arrive at a solution.
Below is my code that I am trying, any help is greatly appreciated.
private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_TIME_ZONE = ZoneId.SHORT_IDS.get("EST");
public static void main(String[] args) throws Exception {
String dateTime = "2018-01-15 02:35:00";
//parse the datetime using LocalDateTime
LocalDateTime defaultDateTime = LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(DATE_FORMAT));
//get the datetime in default timezone
ZoneId defaultZoneId = ZoneId.of(DEFAULT_TIME_ZONE);
ZonedDateTime defaultZoneDateTime = defaultDateTime.atZone(defaultZoneId);
System.out.println("EST time: "+defaultZoneDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)));
ZonedDateTime utcZonedDateTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC"));
String utcTime = defaultZoneDateTime.withZoneSameInstant(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern(DATE_FORMAT));
System.out.println("UTC : "+utcTime);
//IST timezone
ZoneOffset offset = ZoneOffset.of("+05:30");
OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);
String targetTimeZone = offsetDate.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
System.out.printf("target time : "+targetTimeZone);
}
OUTPUT
EST time: 2018-01-15 02:35:00
UTC : 2018-01-15 07:37:00
target time : 2018-01-15 07:37:00
Expected target time : 2018-01-15 13:05:00
The immediate problem is this line:
OffsetDateTime offsetDate = OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset);
That's saying you want the same local date/time, but with the specified offset. That changes which instant in time is being represented.
Instead, you really want to represent the same instant in time, but at a particular offset. So the shortest fix is:
OffsetDateTime offsetDate = utcZonedDateTime.toInstant().atOffset(offset);
However, there are a number of other aspects which could do with changing:
Prefer ZoneOffset.UTC to ZoneId.of("UTC")
Using EST as a time zone is confusing - it's not clear whether you expect it to mean "Eastern Time" (changing between EST and EDT) or pure standard time of UTC-5. Assuming you actually mean "Eastern Time" it would be better to use America/New_York as a zone ID.
It's unclear what you want to happen if the input string represents a skipped or ambiguous value in Eastern time. These happen around DST transitions.
Next, you don't need to convert the ZonedDateTime in Eastern time into a ZonedDateTime in UTC at all. Either convert it directly to an instant:
OffsetDateTime target = defaultZoneDateTime.toInstant().at(offset);
Or create a ZonedDateTime for the target instead:
ZonedDateTime target = defaultZoneDateTime.withZoneSameInstant(offset);
Given that an offset isn't really a time zone, I'd probably go with the first of these.
You're using
OffsetDateTime.of(utcZonedDateTime.toLocalDateTime(), offset)
to create your target. You're thus constructing an OffsetDateTime in the target offset, having a LocalDateTime equal to the LocalDateTime in the UTC zone.
What you want is the exact same transformation as the one you're using to go from the EST time to UTC: keep the same instant, but go to a different timezone:
defaultZoneDateTime.withZoneSameInstant(offset);
or, if you really want an OffsetDateTime and not a ZonedDateTime:
OffsetDateTime.ofInstant(defaultZoneDateTime.toInstant(), offset);

Java - SimpleDateFormat formatter to return epoch time with milliseconds [duplicate]

This question already has answers here:
Convert a date format in epoch
(6 answers)
Closed 5 years ago.
I am very new to Java and coding in general - I have some code which returns a timestamp in the following format yyyy.MM.dd HH:mm:ss:ms which is shown below:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");
This returns:
2017.07.19 11:42:30:423
Is there a way to edit the "SimpleDateFormat formatter" code above to return the date/time as an epoch timestamp that includes milliseconds so that the value returned is formatted as per the below?
1500464550423
I'm hoping that I can amend the ("yyyy.MM.dd HH:mm:ss:sss") part of the SimpleDateFormat formatter code to do this.
Any help or advice is much appreciated.
Thanks
You have a simple error in the use of case in your format pattern string (these are case sensitive). And worse, you are using the old and troublesome SimpleDateFormat class. One of the many problems with it is it’s not telling you what the problem is.
So I recommend you use the modern Java date and time API instead (I am deliberately using your format pattern string verbatim):
String receivedTimetamp = "2017.07.19 11:42:30:423";
DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:sss");
LocalDateTime dateTime = LocalDateTime.parse(receivedTimetamp, parseFormatter);
System.out.println(dateTime);
This code throws an IllegalArgumentException: Too many pattern letters: s. I hope this calls your awareness to the fact that you are using two s’s for seconds and three s’s for fraction of second. If it still isn’t clear, the documentation will tell you that lowercase s is correct for seconds, while you need uppercase S for the fraction. Let’s repair:
DateTimeFormatter parseFormatter
= DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
Now the code prints 2017-07-19T11:42:30.423, so we have managed to parse the string correctly.
To convert to milliseconds we are still missing a crucial piece of information: in what time zone should the timestamp be interpreted? I think the two obvious guesses are UTC and your local time zone (which I don’t know). Try UTC:
System.out.println(dateTime.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli());
This produces 1500464550423, which is the number you asked for. I suppose we’re done.
If you wanted your JVM’s time zone setting instead, use .atZone(ZoneId.systemDefault()) instead of .atOffset(ZoneOffset.UTC), but beware that the setting may be altered by other software running in the same JVM, so this is fragile.
First of all, check the documentation of SimpleDateFormat. The pattern that corresponds to milliseconds is an uppercase S, while the lowercase s corresponds to seconds. The problem is that SimpleDateFormat usually doesn't complain and try to parse 423 as seconds, adding this amount to your end date (giving an incorrect result).
Anyway, SimpleDateFormat just parses a String to a java.util.Date or formats the Date to a String. If you want the epoch millis value, you must get it from the Date object:
// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
// parse to a date
Date date = formatter.parse(s);
// get epoch millis
long millis = date.getTime();
System.out.println(millis); // 1500475350423
The problem is that SimpleDateFormat uses the system's default timezone, so the final value above (1500475350423) will be equivalent to the specificed date and time in my system's timezone (which can be different from yours - just for the record, my system's default timezone is America/Sao_Paulo). If you want to specify in what timezone this date is, you need to set in the formatter (before calling parse):
// set a timezone to the formatter (using UTC as example)
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
With this, the result for millis will be 1500464550423 (the equivalent to the specificed date and time in UTC).
To do the opposite (create a date from the millis value), you must create a Date object and then pass it to the formatter (also taking care of setting a timezone to the formatter):
// create date from millis
Date date = new Date(1500464550423L);
// use correct format ('S' for milliseconds)
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS");
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
// format date
String formatted = formatter.format(date);
Java new date/time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
As the input String has no timezone information (only date and time), first I parsed it to a LocalDateTime (a class that represents a date and time without timezone). Then I convert this date/time to a specific timezone and get the millis value from it:
// input string
String s = "2017.07.19 11:42:30:423";
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// as the input string has no timezone information, parse it to a LocalDateTime
LocalDateTime dt = LocalDateTime.parse(s, formatter);
// convert the LocalDateTime to a timezone
ZonedDateTime zdt = dt.atZone(ZoneId.of("Europe/London"));
// get the millis value
long millis = zdt.toInstant().toEpochMilli(); // 1500460950423
The value is now 1500460950423, equivalent to the specified date and time in London timezone.
Note that the API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use ZoneOffset.UTC constant if you want to use UTC.
To do the opposite, you can get the millis value to create an Instant, convert it to a timezone and pass it to the formatter:
// create Instant from millis value
Instant instant = Instant.ofEpochMilli(1500460950423L);
// use correct format ('S' for milliseconds)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss:SSS");
// convert to timezone
ZonedDateTime z = instant.atZone(ZoneId.of("Europe/London"));
// format
String formatted = z.format(formatter);
First advice is to move to java8 java.time API instead of learning the broken java.date API
then do:
Instant i = Instant.now();
System.out.println(i.toEpochMilli());
in your case you can do:
LocalDateTime myldt = LocalDateTime.parse("2017-06-14 14:29:04",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(myldt.toInstant(ZoneOffset.UTC).toEpochMilli());
note that as soon as you play more with the api you will find more ways to achieve the same thing, at the end you will end invoking toEpochMilli
String strDate = "Jun 13 2003 23:11:52.454 UTC";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
ZonedDateTime zdt = ZonedDateTime.parse(strDate,dtf);
System.out.println(zdt.toInstant().toEpochMilli()); // 1055545912454
You can try
long time = System.currentTimeMillis();
If you have a java.util.Date then invoking getTime() will return the number of millis since the epoch. For example:
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:sss");
Date dateToBeFormatted = new Date();
// this will print a datetime literal on the above format
System.out.println(formatter.format(dateToBeFormatted));
// this will print the number of millis since the Java epoch
System.out.println(dateToBeFormatted.getTime());
The key point here is that in order to get the number of millis since the epoch you do not need a SimpleDateFormatter because the number of millis since the epoch is a property of the Date.

Categories

Resources