I am getting a time string that is the sunrise/sunset times in UTC in format HH:mm
example:
09:35
Currently I am doing this to convert the given time to the current date UTC using the java.time library
val utcZoneId = ZoneId.of("UTC")
val now = Instant.now()
val dateTimeFormater:DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(utcZoneId)
val date = dateTimeFormater.format(now)
val fullDateSunrise = "${date}T${data[0].sunrise}:00"
val local = LocalDateTime.parse(fullDateSunrise, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
val final = local.atZone(utcZoneId)
val utcSunrise = final.toInstant().toEpochMilli()
val fullDateSunset = "${date}T${data[0].sunset}:00"
val local2 = LocalDateTime.parse(fullDateSunset, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
val final2 = local2.atZone(utcZoneId)
val utcSunset = final2.toInstant().toEpochMilli()
I then pass the UTC milliseconds back the the client once I have them
It works how I need it to but I can help but feel there must be an easier way than getting a formatted UTC date string and combining that with the given time and then converting that to an actual DateTime object.
So the question is, is there an easier way to do this?
Sure, you definitely don't need to parse back and forth to strings. I assume an input of 09:35 means: At 09:35, local time, the sun will rise. Note that you're confusing things; UTC is a zone, an input like 09:35 is zoneless. I doubt this stamp represent 'UTC'; it would mean that the correct value for the sunrise today for Tokyo is -5:25, as it'll be 19:25, the previous day, in the UTC timezone when the sun rose in tokyo today.
Once you stop using the UTC zone it becomes muuuch simpler:
DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
LocalDateTime sunriseToday = LocalDateTime.now().with(LocalTime.parse("04:35", TIME_FORMAT));
ZonedDateTime inTokyo = sunriseToday.atZone(ZoneId.of("Asia/Tokyo"));
return inTokyo.toInstant().toEpochMilli();
Note that this would return the exact moment in time when the sun rises in tokyo. Printing it as an ISO stamp, that'd be 2020-06-09T19:35Z.
If you really want the epoch-millis that match 2020-06-10T04:35Z - which to be clear makes no sense, that is NOT when the sun rose in tokyo at all today! - then...
DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH:mm");
LocalDateTime sunriseToday = LocalDateTime.now().with(LocalTime.parse("04:35", TIME_FORMAT));
ZonedDateTime inTokyo = sunriseToday.atZone(ZoneId.of("Asia/Tokyo"));
ZoneDateTime thisMakesNoSense = inTokyo.withZoneSameLocal(ZoneOffset.UTC);
return thisMakesNoSense.toInstant().toEpochMilli();
You don't have to convert Strings, use a ZonedDateTime instead and provide a desired zone.
Use some fun like this one:
fun convertToEpochMillis(time: String, zoneId: ZoneId): Long {
// parse the time to a LocalTime
val localTime = LocalTime.parse(time, DateTimeFormatter.ofPattern("HH:mm"))
// create a ZonedDateTime from the current date, the parsed time the given time zone
val zonedDateTime = ZonedDateTime.of(LocalDate.now(), localTime, zoneId)
// then return the representation of the instant in epoch millis
return zonedDateTime.toInstant().toEpochMilli()
}
and use it in a fun main() as follows
fun main() {
// define / receive a time
val time = "09:35"
// and a zone identifier
var zone = "UTC"
// use the method
val utcSunriseMillis = convertToEpochMillis(time, ZoneId.of(zone))
// and print a result statement
println("Sunrise time ${time} in ${zone} is ${utcSunriseMillis}")
// change the zone and do the same again with a different zone, just to see what happens...
zone = "America/Los_Angeles"
val laSunriseMillis = convertToEpochMillis(time, ZoneId.of(zone))
println("Sunrise time ${time} in ${zone} is ${laSunriseMillis}")
}
which then prints today (=> 2020-06-10)
Sunrise time 09:35 in UTC is 1591781700000
Sunrise time 09:35 in America/Los_Angeles is 1591806900000
Related
I have two timestamps and a localized date that I use to find the timezone offset and add it to these dates. How can I calculate the time offset between dates simpler? My method doesn't work with negative values (if (tsOffset.toSecondOfDay() > 0 always true).
fun parseDateTime(startTs: Long, endTs: Long, localizedDateTime: String): Pair<String, String> {
val dateUtcStart = LocalDateTime.ofInstant(Instant.ofEpochMilli(startTs), ZoneOffset.UTC)
val dateUtcEnd = LocalDateTime.ofInstant(Instant.ofEpochMilli(endTs), ZoneOffset.UTC)
val formatter = DateTimeFormatterBuilder()
.parseCaseInsensitive()
.parseDefaulting(ChronoField.YEAR_OF_ERA, dateUtcStart.year.toLong())
.append(DateTimeFormatter.ofPattern("E dd MMM hh:mm a"))
.toFormatter(Locale.ENGLISH)
val localDateTime = LocalDateTime.parse(localizedDateTime, formatter)
val localTs = Timestamp.valueOf(localDateTime).time
val tsOffset = LocalTime.ofInstant(Instant.ofEpochMilli(localTs - startTs), ZoneOffset.systemDefault())
val tzString = if (tsOffset.toSecondOfDay() > 0) "+$tsOffset" else tsOffset.toString()
val startDate = dateUtcStart.toString() + tzString
val endDate = dateUtcEnd.toString() + tzString
return Pair(startDate, endDate)
}
#Test
fun parseDateTime() {
val pair1 = parseDateTime(1626998400000, 1627005600000, "Fri 23 Jul 10:30 am")
val pair2 = parseDateTime(1626998400000, 1627005600000, "Thu 22 Jul 11:30 pm")
// pass
assertEquals("2021-07-23T00:00+10:30", pair1.first)
assertEquals("2021-07-23T02:00+10:30", pair1.second)
// fails
assertEquals("2021-07-23T00:00-00:30", pair2.first)
assertEquals("2021-07-23T02:00-00:30", pair2.second)
}
Also I tried
val dur = Duration.between(dateUtcStart, localDateTime)
But don't sure how to convert it in the string or add to the dates properly.
Here startTs - start of an event timestamp. endTs - end of this event timestamp. localizedDateTime is used to show the start time in the actual time zone (real city) while timestamps show time in UTC. I need to extract this timezone from localizedDateTime and add it to start and end string dateTimes (start = "2021-07-23T00:00+10:30", end = "2021-07-23T02:00+10:30" for startTs = 1626998400000 and endTs = 1627005600000 accordingly).
It’s no simpler than your code, on the contrary, but it fixes a couple of issues that you had.
Disclaimer: I haven’t got your Pair class and I cannot write nor run Kotlin. So I am printing the resulting strings from withint the method, which you will have to change for your purpose. And you will have to hand translate my Java.
private static void parseDateTime(long startTs, long endTs, String localizedDateTime) {
// startTs and localizedDateTime are both representations of the event start time.
// Use this information to obtain the UTC offset of the local time.
Instant startInstant = Instant.ofEpochMilli(startTs);
OffsetDateTime startUtc = startInstant.atOffset(ZoneOffset.UTC);
// Corner case: the local year may be different from the UTC year if the event is close to New Year.
// Check whether this is the case. First get the UTC month and the local month.
Month utcMonth = startUtc.getMonth();
DateTimeFormatter baseSyntaxFormatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.append(DateTimeFormatter.ofPattern("E dd MMM hh:mm a"))
.toFormatter(Locale.ENGLISH);
Month localMonth = baseSyntaxFormatter.parse(localizedDateTime, Month::from);
int utcYear = startUtc.getYear();
int localYear;
if (utcMonth.equals(Month.DECEMBER) && localMonth.equals(Month.JANUARY)) {
// Local date is in the following year
localYear = utcYear + 1;
} else if (utcMonth.equals(Month.JANUARY) && localMonth.equals(Month.DECEMBER)) {
localYear = utcYear - 1;
} else {
localYear = utcYear;
}
DateTimeFormatter finalFormatter = new DateTimeFormatterBuilder()
.append(baseSyntaxFormatter)
.parseDefaulting(ChronoField.YEAR_OF_ERA, localYear)
.toFormatter(Locale.ENGLISH);
LocalDateTime startLocal = LocalDateTime.parse(localizedDateTime, finalFormatter);
// Now calculate offset
Duration durationOfOffset = Duration.between(startUtc.toLocalDateTime(), startLocal);
ZoneOffset offset = ZoneOffset.ofTotalSeconds(Math.toIntExact(durationOfOffset.getSeconds()));
String startString = startLocal.atOffset(offset).toString();
// Calculate end date and time
String endString = Instant.ofEpochMilli(endTs)
.atOffset(offset)
.toString();
System.out.format("%s - %s%n", startString, endString);
}
Let’s try it with your example data:
parseDateTime(1_626_998_400_000L, 1_627_005_600_000L, "Fri 23 Jul 10:30 am");
parseDateTime(1_626_998_400_000L, 1_627_005_600_000L, "Thu 22 Jul 11:30 pm");
Output:
2021-07-23T10:30+10:30 - 2021-07-23T12:30+10:30
2021-07-22T23:30-00:30 - 2021-07-23T01:30-00:30
You notice that the local start times are now 10:30 and 23:30 as in your input strings, which I believe corrects an error that you had.
Let’s also try an example that bridges New Year:
Instant start = Instant.parse("2021-01-01T00:00:00Z");
parseDateTime(start.toEpochMilli(),
start.plus(2, ChronoUnit.HOURS).toEpochMilli(),
"Thu 31 Dec 10:30 pm");
2020-12-31T22:30-01:30 - 2021-01-01T00:30-01:30
I have used basically the same way of calculating the offset that you present in your own answer.
Issues with your code
First, as I said, the times that your unit test asserts do not agree with the times in your input. For the required output you have taken the UTC time of day and combined with the local offset, which gives a different point in time. A UTC offset is always used with a time of day at that offset (for UTC time of day one would use either offset Z or +00:00).
Consider having your method return a Pair<OffsetDateTime, OffsetDateTime>, not a Pair<String, String>. Strings are for presentation to the user and sometimes for data exchange. Inside your program you should use proper date-time objects, not strings.
As I said in the comments, New Year doesn’t happen at the same time in all time zones. So the year that you get from dateUtcStart.year.toLong() needs not be the year that was assumed in the string from a different time zone. My code takes this into account.
Don’t involve the Timestamp class. It’s poorly designed and long outdated. All it gives you is an extra conversion, hence extra complication.
Your basic problem was using a LocalTime for a duration that might be positive or negative. A LocalTime is for a time of day, not for an amount of time. Your own solution already got rid of this problem.
This is wrong:
val startDate = dateUtcStart.toString() + tzString
First, you should not want to use string manipulation for date and time math. The classes from java.time that you are using produce the string that you need more easily and with less risk of errors. Second, you have indeed got an error here: you are appending the calculated offset to a string that is in UTC, that is, assumes offset 0 (or Z). This was why you were in fact able to produce the incorrect results that your unit test expected.
I got an answer on the Kotlin forum:
val duration = Duration.between(dateUtcStart, localDateTime)
val offset = ZoneOffset.ofTotalSeconds(duration.seconds.toInt())
val startDate = dateUtcStart.atOffset(offset).toString()
val endDate = dateUtcEnd.atOffset(offset).toString()
I have been trying to convert local time (EST) to UTC and vice versa. So, I present a time picker, user selects the time, I convert the same to UTC and send to server. Here's the code:
val cal = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, mHour) //mHour = 15
cal.set(Calendar.MINUTE, mMinute) //mMinute = 00
cal.set(Calendar.SECOND, 0)
val formatter = SimpleDateFormat("HH:mm:ss")
formatter.timeZone = TimeZone.getTimeZone("UTC")
val cutOffTime = formatter.format(cal.time) //this gives 19:00:00 which is correct
Above output for cutOffTime is correct as 15:00 EST is 19:00 UTC after considering day light savings.
Now, I fetch this same cutOffTime from server, convert it to local (EST) and display. Here's the code:
val cutOffTime = jsonObject.get("cutOffTime").getAsString()) //value is 19:00:00
var cutoffTime: Time? = null
val format = SimpleDateFormat("hh:mm:ss")
format.timeZone = TimeZone.getTimeZone("UTC")
cutoffTime = Time(format.parse(cutOffTime).time)
//cutoffTime has value 14:00 which is strange, it should be 15:00
So, cutoffTime in above code has value 14:00 which is strange, it should be 15:00.
Please note that this code was working before day light savings on March 8, 2020. Any idea what am I doing wrong?
Please don't use the old and badly designed java api for date and times. Use the new java.time api. It's much more robust and is nicer to use.
Here is an example on how to do your code with java.time:
int mHour = 15;
int mMinute = 0;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
ZonedDateTime toFormat = LocalDateTime
.now() // Current date and time.
.withHour(mHour).withMinute(mMinute).withSecond(0) // Change hour, minute and second like in your example.
.truncatedTo(ChronoUnit.SECONDS) // Throw away milliseconds and nanoseconds.
.atZone(ZoneOffset.UTC); // Say that the time is located at UTC+0.
String formatted = formatter
.withZone(ZoneId.of("America/New_York")) // Create a new formatter that formats toFormat to the local time America/New_York.
.format(toFormat);
System.out.println(formatted); // Gives 11:00:00 on my machine now. America/New_York is either UTC-5 or UTC-4.
ZonedDateTime parsed = LocalTime
.parse(formatted, formatter) // Parse the String. The result is an instance of LocalTime.
.atDate(LocalDate.now(ZoneId.of("America/New_York"))) // Add the date values which are those of the current local date at America/New_York.
.atZone(ZoneId.of("America/New_York")); // Create a ZonedDateTime of the LocalDateTime to explicitly define the zone.
Instant pointInTimeA = parsed.toInstant();
Instant pointInTimeB = toFormat.toInstant();
System.out.println(pointInTimeA.equals(pointInTimeB)); // Gives true because both describe the same point in time as they should.
The great benefit is that the api will handle everything regarding summertime and wintertime for you. Have read here for information about the EST and why you shouldn't use it.
The problem you face is probably due to the fact that you used HH in one formatter but hh in the other one. Those are not the same. Have read here about Patterns for Formatting and Parsing.
I ended up with below solution. I knew the format of the string from server, so I split it. I didn't want to use the new time API since it is available from API level 26 onwards.
val cutOffString = jsonObject.get("cutOffTime").getAsString() //UTC date 19:00:00 from server
val cal = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, cutOffString.split(":")[0].toInt())
cal.set(Calendar.MINUTE, cutOffString.split(":")[1].toInt())
cal.set(Calendar.SECOND, 0)
cal.timeZone = TimeZone.getTimeZone("UTC")
val cutoffTime: Time = Time(cal.time.time) //15:00:00
My current date:
Date utc: 2018-06-06T16:30:00Z (ISO 8601 in UTC)
OR
Date iso: 2018-06-06T11:30:00-05:00 (ISO 8601)
OR
Date epoch: 1528302600000 (Epoch/Unix Timestamp)
I wish to convert the above DateTime to some another time zone areas (like GMT+5:30). And I'm not sure which time format I'll receive from above three. So can I've a generic method which can convert above to some another time zone returning java.util.Date in Java 8?
I did Something like this, But it didn't worked out
public Date convertDateToLocalTZ(Date iso8601, ZoneId toZoneId) {
Date dateTime = null;
if (iso8601 != null && toZoneId != null) {
Instant instant = iso8601.toInstant();
LocalDateTime localDateTime = instant.atZone(toZoneId).toLocalDateTime();
dateTime = Date.from(localDateTime.atZone(toZoneId).toInstant());
return dateTime;
}
return dateTime;
}
Since question is tagged java-8 use java.time API.
UPDATE: For version 4 of question where 2018-06-06T11:30:00-05:00 was added.
To parse 1528302600000, you parse it into a long, then use Instant.ofEpochMilli().
To parse a format like 2018-06-06T11:30:00-05:00, you can using OffsetDateTime or ZonedDateTime. Both can also parse 2018-06-06T16:30:00Z.
To change the time zone specifically to a particular offset like GMT+5:30, use ZoneOffset, e.g. ZoneOffset.of("+05:30"), or ZoneId, e.g. ZoneId.of("GMT+05:30").
Note 1: GMT+5:30 is not valid.
Note 2: To change to the time zone of a region, honoring Daylight Savings Time, use e.g. ZoneId.of("Asia/Kolkata").
To parse all 3 input formats, and even support the extended format like 2018-06-06T11:30-05:00[America/Chicago], use ZonedDateTime, with special handling for the epoch number.
public static ZonedDateTime parseToZone(String text, ZoneId zone) {
if (text.indexOf('-') == -1)
return Instant.ofEpochMilli(Long.parseLong(text)).atZone(zone);
return ZonedDateTime.parse(text).withZoneSameInstant(zone);
}
The caller can then decide if only the offset, not the full time zone, should be used, by converting it to OffsetDateTime using toOffsetDateTime().
Test
ZoneId india = ZoneId.of("Asia/Kolkata");
System.out.println(parseToZone("2018-06-06T16:30:00Z", india));
System.out.println(parseToZone("2018-06-06T11:30:00-05:00", india));
System.out.println(parseToZone("1528302600000", india));
System.out.println(parseToZone("1528302600000", india).toOffsetDateTime());
Output
2018-06-06T22:00+05:30[Asia/Kolkata]
2018-06-06T22:00+05:30[Asia/Kolkata]
2018-06-06T22:00+05:30[Asia/Kolkata]
2018-06-06T22:00+05:30
Original Answer
Use the parse() method with 2018-06-06T16:30:00Z.
Use the ofEpochMilli() method with 1528302600000.
Then use atZone() to convert to your desired time zone.
Demo
Instant instant1 = Instant.parse("2018-06-06T16:30:00Z");
Instant instant2 = Instant.ofEpochMilli(1528302600000L);
ZoneId india = ZoneId.of("Asia/Kolkata");
ZonedDateTime date1 = instant1.atZone(india);
ZonedDateTime date2 = instant2.atZone(india);
System.out.println(instant1);
System.out.println(instant2);
System.out.println(date1);
System.out.println(date2);
Output
2018-06-06T16:30:00Z
2018-06-06T16:30:00Z
2018-06-06T22:00+05:30[Asia/Kolkata]
2018-06-06T22:00+05:30[Asia/Kolkata]
To print the result in human format, use a DateTimeFormatter.
DateTimeFormatter indiaFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.forLanguageTag("en-IN"));
DateTimeFormatter hindiFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.forLanguageTag("hi-IN"));
System.out.println(date1.format(indiaFormatter));
System.out.println(date1.format(hindiFormatter));
Output
6 June 2018 at 10:00:00 PM IST
6 जून 2018 को 10:00:00 अपराह्न IST
In Java 8+, you should use the new java.time API.
Your initial UTC time must be modelized as an Instant. Use DateTimeFormatter to parse from a string like 2018-06-07T22:21:00Z if needed, or get the current Instant with Instant.now.
Then you can use Instant.atZone or Instant.withOffset to convert to a ZonedDateTime resp. OffsetDateTime with the desired time shift. ZonedDateTime helps you get the date/time at a given region/country, while OffsetDateTime makes a purely numerical time shift independent from location and daylight saving time.
What I'm trying to do: Check for the minutes until an event. (I'm in central time which is UTC -5 hours).
The object I get is a JSON Element that looks like this when I take the string:
/Date(1502964420000-0500)/
I should be able to:
//take the departure time and subtract it from the current time. Divide by 60
timeStamp = timeStamp.substring(6,16);
This gives me 1502964420 which I can use a time converter to get: Thursday, August 17, 2017 5:07:00 AM
Problem is.. How do I get the current time in the same format to subtract it?
(or if there's a better way to do this I'd gladly take that advice as well).
I would recommend looking at the datatype ZonedDateTime.
With this you can easily perform calculasions and conversions like this:
ZonedDateTime startTime = ZonedDateTime.now();
Instant timestamp = startTime.toInstant(); // You can also convert to timestamp
ZonedDateTime endTime = startTime.plusSeconds(30);
Duration duration = Duration.between(startTime, endTime);
if(duration.isNegative()){
// The end is before the start
}
long secondsBetween = duration.toMillis(); // duration between to seconds
Since you don't know about ZonedDateTime here is a quick overview how to convert string to ZonedDateTime:
Note: The String has to be in the ISO8601 format!
String example = "2017-08-17T09:14+02:00";
OffsetDateTime offset = OffsetDateTime.parse(example);
ZonedDateTime result = offset.atZoneSameInstant( ZoneId.systemDefault() );
You can either use Date currentDate = new Date() and then currentDate.getTime() to get the current Unix time in milliseconds or use the Calendar-class: Calendar currentDate = Calendar.getInstance() and currentDate.getTime().getTime() to get the current Unix time in milliseconds.
You can do the same with the date parsed from the json and then calculate the difference between the two values. To get the difference in minutes, just divide it then by (60*1000)
I have time stamp in format 2017-18-08 11:45:30.345.
I want to convert it to epoch time, so I am doing below:
String timeDateStr = "2017-18-08 11:45:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
ZonedDateTime zdt = ZonedDateTime.parse(timeDateStr, dtf);
System.out.println(zdt.toInstant().toEpochMilli());
I am getting below error:
java.time.format.DateTimeParseException: Text '2017-18-08 11:45:30.345' could not be parsed: Unable to obtain ZonedDateTime from TemporalAccessor
I also tried different formats but still getting errors.
Note: originally the question had the input 2017-18-08 12:60:30.345 (with 60 in the minutes field), then it was edited (the time changed from 12:60 to 11:45), but I decided to keep this answer discussing about the original input (12:60), as it also works for the edited version (11:45).
ZonedDateTime needs a timezone or offset, but the input String doesn't have it (it has only date and time).
There are also another details in the input:
the minute value is 60, which is not accepted: the valid values are from 0 to 59 (actually there's a way to accept this, see "Lenient parsing" below)
the hh is the clock-hour-of-am-pm field, so it also needs the AM/PM designator to be fully resolved. As you don't have it, you should use the HH pattern instead
So the pattern must be yyyy-dd-MM HH:mm:ss.SSS, the input can't have 60 as the minutes value (unless you use lenient parsing, which I'll explain below) and you can't direclty parse it to a ZonedDateTime because it doesn't have a timezone/offset designator.
One alternative is to parse it to a LocalDateTime and then define in which timezone/offset this date is. In the example below, I'm assuming it's in UTC:
// change 60 minutes to 59 (otherwise it doesn't work)
String timeDateStr = "2017-18-08 12:59:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
// assume the LocalDateTime is in UTC
Instant instant = dt.toInstant(ZoneOffset.UTC);
System.out.println(instant.toEpochMilli());
This will output:
1503061170345
Which is the equivalent of 2017-18-08 12:59:30.345 in UTC.
If you want the date in another timezone, you can use the ZoneId class:
// get the LocalDateTime in some timezone
ZonedDateTime z = dt.atZone(ZoneId.of("Europe/London"));
System.out.println(z.toInstant().toEpochMilli());
The output is:
1503057570345
Note that the result is different, because the same local date/time represents a different Instant in each timezone (in each part of the world, the local date/time 2017-18-08 12:59:30.345 happened in a different instant).
Also note that 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 the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
There's also the option to convert the LocalDateTime to an offset (like -05:00 or +03:00):
// get the LocalDateTime in +03:00 offset
System.out.println(dt.toInstant(ZoneOffset.ofHours(3)).toEpochMilli());
The output will be equivalent to the local date/time in the offset +03:00 (3 hours ahead of UTC):
1503050370345
Lenient parsing
As #MenoHochschild reminded me in the comments, you can use lenient parsing to accept 60 in the minutes field (using the java.time.format.ResolverStyle class):
String timeDateStr = "2017-18-08 12:60:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS")
// use lenient parsing
.withResolverStyle(ResolverStyle.LENIENT);
// parse to LocalDateTime
LocalDateTime dt = LocalDateTime.parse(timeDateStr, dtf);
In this case, 60 minutes are adjusted to the next hour, and the LocalDateTime will be:
2017-08-18T13:00:30.345
Daylight Saving Time
If you decide to use UTC or a fixed offset (using ZoneOffset class), you can ignore this section.
But if you decide to use a timezone (with ZoneId class), you must also take care of DST (Daylight Saving Time) issues. I'm gonna use the timezone I live in as example (America/Sao_Paulo).
In São Paulo, DST starts at October 15th 2017: at midnight, clocks shift 1 hour forward from midnight to 1 AM. So all local times between 00:00 and 00:59 don't exist in this timezone. If I create a local date in this interval, it's adjusted to the next valid moment:
ZoneId zone = ZoneId.of("America/Sao_Paulo");
// October 15th 2017 at midnight, DST starts in Sao Paulo
LocalDateTime d = LocalDateTime.of(2017, 10, 15, 0, 0, 0, 0);
ZonedDateTime z = d.atZone(zone);
System.out.println(z);// adjusted to 2017-10-15T01:00-02:00[America/Sao_Paulo]
When DST ends: in February 18th 2018 at midnight, clocks shift back 1 hour, from midnight to 23 PM of 17th. So all local times from 23:00 to 23:59 exist twice (in DST and in non-DST), and you must decide which one you want:
// February 18th 2018 at midnight, DST ends in Sao Paulo
// local times from 23:00 to 23:59 at 17th exist twice
LocalDateTime d = LocalDateTime.of(2018, 2, 17, 23, 0, 0, 0);
// by default, it gets the offset before DST ends
ZonedDateTime beforeDST = d.atZone(zone);
System.out.println(beforeDST); // before DST end: 2018-02-17T23:00-02:00[America/Sao_Paulo]
// get the offset after DST ends
ZonedDateTime afterDST = beforeDST.withLaterOffsetAtOverlap();
System.out.println(afterDST); // after DST end: 2018-02-17T23:00-03:00[America/Sao_Paulo]
Note that the dates before and after DST ends have different offsets (-02:00 and -03:00). This affects the value of epochMilli.
You must check when DST starts and ends in the timezone you choose and check the adjustments accordingly.
Corrected your code regarding yyyy-dd-MM. Also minute value could be 1-59 not 60. You provided 60. This is another simple way to solve the issue. Simply use DateFormat class.
String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-MM hh:mm:ss.SSS", Locale.ENGLISH);
try {
Date d = df.parse(timeDateStr);
System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
e.printStackTrace();
}
Just i had made little bit change in nagendra547's answer
Please reffer to below code:-
String timeDateStr = "2017-18-08 12:59:30.345";
DateFormat df = new SimpleDateFormat("yyyy-dd-mm hh:mm:ss.SSS", Locale.ENGLISH);
try {
Date d = df.parse(timeDateStr);
System.out.println(d.toInstant().toEpochMilli());
} catch (ParseException e) {
e.printStackTrace();
}
Your code will fail for below 3 reasons.
Your date string (2017-18-08 12:60:30.345), doesn't match with the Formatter you used. It should be yyyy-MM-dd HH:mm:ss.SSS instead of yyyy-dd-MM hh:mm:ss.SSS
the range of minutes is (0-59), 60 doesn't come in this range.
Even if you have corrected code based above point it won't run for ZonedDateTime. So you would need to create a LocalDateTime before and then pass a ZoneId to it.
The code should look like below:
String timeDateStr = "2017-18-08 12:59:30.345";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-dd-MM HH:mm:ss.SSS");
LocalDateTime date = LocalDateTime.parse(timeDateStr, dtf);
ZonedDateTime zdt = date.atZone(ZoneId.of("Europe/London"));
System.out.println(zdt.toInstant().toEpochMilli());