Converting String to time, changing the timezone, then back to String - java

I have a String containing a time in the format: 08:00:00
This time is from US Eastern time and I want to convert it to London's timezone and end up with a String of that time.
I have converted the String to time using
Time.valueOf(t);
However after this I cannot get the timezone to change.

you can displace the time using withZoneSameInstant
LocalTime myLocalTime = LocalTime.parse("08:00:00", DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime londonTime = LocalDateTime.of(LocalDate.now(), myLocalTime).atZone(ZoneId.of("America/New_York"))
.withZoneSameInstant(ZoneId.of("Europe/London")).toLocalTime();
System.out.println(myLocalTime);
System.out.println(londonTime);

There are lots of details regarding this question.
The Time class sets the date (day, month and year) to January 1st, 1970. But to convert from EST to London local time, you must consider Daylight Saving Time rules.
The difference in hours is not always the same; it can change depending on the date - considering this year (2017): from January 1st to March 11th, the difference will be 5 hours, then from March 12th to March 25th the difference is 4 hours, then it's back to 5 hours, then in October 29th it's 4 hours and in November 5th is 5 hours again, until the end of the year.
That's because of DST starting and ending in both timezones and at different dates. And each year, these dates change as well, so you need to know the date you're working with, to make the correct conversion.
Another thing is that Java 8 new 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 EST) because they are ambiguous and not standard.
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.
In the example below I'm using America/New_York - one of the many timezones that uses EST (there are more than 30 timezones that uses or had used it). You can call ZoneId.getAvailableZoneIds() to check all the timezones and choose one that suits best for your case.
The code is very similar to #ΦXocę 웃 Пepeúpa ツ answer, well, because it's straightforward and there's not much to change. I just wanted to add the insights above.
// timezones for US and UK
ZoneId us = ZoneId.of("America/New_York");
ZoneId uk = ZoneId.of("Europe/London");
// parse the time string
LocalTime localTimeUS = LocalTime.parse("08:00:00");
// the reference date (now is the current date)
LocalDate now = LocalDate.now(); // or LocalDate.of(2017, 5, 20) or any date you want
// the date and time in US timezone
ZonedDateTime usDateTime = ZonedDateTime.of(now, localTimeUS, us);
// converting to UK timezone
ZonedDateTime ukDateTime = usDateTime.withZoneSameInstant(uk);
// get UK local time
LocalTime localTimeUK = ukDateTime.toLocalTime();
System.out.println(localTimeUK);
The output will be 13:00 (the result of localTimeUK.toString()) because toString() omits the seconds if the value is zero.
If you want to always output the seconds, you can use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
String time = fmt.format(localTimeUK);
In this case, the string time will be 13:00:00.
LocalDate.now() returns the current date using your system's default timezone. If you want the current date in a specific zone, you could've called LocalDate.now(us) (or anyzone you want, or even explicit use the default: LocalDate.now(ZoneId.systemDefault()))

Related

TimeStamp.valueOf() method

public static void main(String[] args) {
Timestamp ts = new Timestamp(116, 02, 12, 20, 45, 0, 0);
Date d = new Date();
d.setTime(ts.getTime());
System.out.println(d);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
//System.out.println(ts.getTime());
System.out.println(simpleDateFormat.format(ts));
System.out.println(Timestamp.valueOf(simpleDateFormat.format(ts)));
}
In above code last two lines print different values. Current time zone is CST, I wanted to convert it into UTC. When I convert it Last two lines print different values by one hour i.e., last but one print 13 mar 2:45 am and last print 13 Mar 3:45 am. Why they are different and How can I correct it.
Java 8
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
System.out.println(inst);
This prints
2016-03-13T02:45:00Z
Today you should not (normally) have a need for a Timestamp object. The java.sql.Timestamp class is long outdated. Once we used it for transferring timestamp values with nanosecond precision to and from SQL databases. Today we use the Instant class for this instead. Instant is one of the classes of java.time, the modern Java date and time API (sometimes we use LocalDateTime from the same API, it depends on your exact requirements and the datatype of your database column).
Neither a Timestamp nor an Instant have a time zone in them. Unlike Timestamp the Instant always prints in UTC (denoted by the Z at the end of the above output). As you can see, the above snippet has correctly converted your time of 20:45 CST to 02:45 the next day UTC.
If you do need a timestamp, typically for a legacy API that you cannot change or don’t want to change just now, conversion is easy:
Timestamp ts = Timestamp.from(inst);
System.out.println(ts);
2016-03-12 20:45:00.0
Timestamp.toString uses the JVM’s time zone setting for generating the string, so you recognize the time we started out from. So the Timestamp contains the correct point in time. There is no need to convert it in any way. If it gets inserted incorrectly into your database, the problem is with your JDBC driver, your database or somewhere else, and you should prefer to correct it there if you can.
Java 6 and 7
Code very similar to the above will work in Java 7 if you add ThreeTen Backport to your project. This is the backport of the java.time classes to Java 6 and 7, and I include a link at the bottom (it’s ThreeTen for JSR-310, where the modern API was first described).
Instant inst = LocalDateTime.of(2016, Month.MARCH, 12, 20, 45)
.atZone(ZoneId.of("America/Chicago"))
.toInstant();
Timestamp ts = DateTimeUtils.toSqlTimestamp(inst);
You notice that the only difference from Java 8 is the way we convert the Instant to a Timestamp. The result is the same, of course.
I you don’t want a dependency on ThreeTen Backport, there are of course still ways to obtain a Timestamp. I wouldn’t use the deprecated constructor, as you do in your code, even though it works as long as no one tampers with your JVM’s time zone setting. If you know you want a Timestamp equal to 02:45 UTC, one option is
Timestamp ts = Timestamp.valueOf("2016-03-12 20:45:00");
It still depends on your JVM’s time zone setting, though.
What went wrong in your code?
As mentioned a Timestamp hasn’t got a time zone in it, so converting a Timestamp to UTC does not make sense.
What happens in your code:
The deprecated Timestamp constructor uses your JVM’s time zone setting (America/Chicago, I presume) for constructing a Timestamp corresponding 12 March 2016 at 8.45 PM in your time zone (the same point in time as 13 March 2:45 AM UTC).
Your SimpleDateFormat correctly formats this into 2016-03-13 02:45:00 (UTC).
Timestamp.valueOf() too uses America/Chicago time zone. However, on the night between 12 and 13 March summer time (daylight saving time) begins in this time zone. At 2 AM the clock is moved forward to 3. So there is no 2:45 this night. Timestamp picks 3:45 instead.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.timeto Java 6 and 7.
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.

Hours difference while considering the date

I need to extract the date field from DB and store it in a VO. How can I compare the hours difference from two dates.
For ex:
Let's say date1 = 01-SEP-17 10:00:00 and date2 = 05-SEP-17 12:00:00. I need to compare the two dates and perform some operations like:
if(hours>10){
//do something
}
if(hours<10){
//do something else
}
I'm just able to calculate the difference between the hours (date2-date1) as 2 but how to consider the date too while calculating the difference between the hours?
My present code:
Date dateA = someVO.getDate();
long date = System.currentTimeMillis();
SimpleDateFormat df = new SimpleDateFormat("dd-MM-YY HH:mm:ss");
Date date1 = new Date(date);
Date date2 = df.parse(dateA.toString());
long date1Hours = date1.getHours();
long date2Hours = date2.getHours();
long dateDiff = date1Hours-date2Hours;
if(dateDiff>10){
//something
}
else if(dateDiff<10){
//something else
}
Easy enough to do using the new Java-Time API added in Java 8:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("dd-MMM-yy HH:mm:ss")
.toFormatter(Locale.US);
LocalDateTime date1 = LocalDateTime.parse("01-SEP-17 10:00:00", fmt);
LocalDateTime date2 = LocalDateTime.parse("05-SEP-17 12:00:00", fmt);
long hours = ChronoUnit.HOURS.between(date1, date2);
System.out.println(hours);
Output
98
First you need to change the pattern used in SimpleDateFormat, and also use a java.util.Locale to specify that the month name is in English (otherwise it uses the system default locale, and it's not guaranteed to always be English).
Then you get the correspondent millis value of each Date, calculate the difference between them and convert this to hours, using a java.util.concurrent.TimeUnit:
SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yy HH:mm:ss", Locale.ENGLISH);
Date date1 = df.parse("01-SEP-17 10:00:00");
Date date2 = df.parse("05-SEP-17 12:00:00");
// get the difference in hours
long dateDiff = TimeUnit.MILLISECONDS.toHours(date2.getTime() - date1.getTime());
dateDiff will be 98.
If you want to compare with the current date, just use new Date().
Daylight Saving Time issues
There's one problem with this approach. Although it doesn't make a difference for most part of the year, there can be differences due to Daylight Saving Time changes.
By default, SimpleDateFormat uses the JVM default timezone. If between the 2 dates there's a Daylight Saving Time changeover (or just an offset change), the result might be different.
Example: in Africa/Windhoek timezone, in September 3rd 2017, at 2 AM, clocks shifted 1 hour forward, from 2 AM to 3 AM (and the offset changed from +01:00 to +02:00). This means that, at that day, all local times between 2 AM and 2:59 AM don't exist in this timezone (it's like they "skipped" this hour).
So, if the JVM default timezone is Africa/Windhoek, then the difference using the code above will be 97 hours (and not 98).
Even if your JVM default timezone is not Africa/Windhoek, this can still happen, depending on the timezone and the dates involved.
Not only that, but the default timezone can be changed without notice, even at runtime. It's always better to specify which timezone you're working with instead of just relying on the default.
You can't avoid DST effects (unless you use UTC), but at least you can choose which timezone you're going to use instead of relying on the system default (that can be changed without notice).
It's possible to set a timezone in the formatter, so all dates will be parsed taking this timezone into account. In the example below, I'm using Europe/London, but of course you can change to one that best suits your case:
// set Europe/London timezone in the SimpleDateFormat
df.setTimeZone(TimeZone.getTimeZone("Europe/London"));
Now all the parsed dates will be considered to be in London timezone (but remind that DST effects will still be considered - the advantage is that you know what timezone you're using and any changes in the JVM's default won't make your code suddenly start giving different and unexpected results).
Always use IANA timezones names (always in the format Continent/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 all timezones using TimeZone.getAvailableIDs() - then you can choose the one that best suits your case.
If you don't want to consider DST effects, you can use TimeZone.getTimeZone("UTC") - because UTC is a standard without DST changes.
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.
First you need to parse the inputs (using a DateTimeFormatter) and specify in what timezone they are. As the dates also have a timezone, I'm using a ZonedDateTime, which is the best choice for this case.
Then you can easily calculate the difference in hours using a ChronoUnit. In the example below, I'm also using London timezone as an example:
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
// case insensitive for month name in all caps
.parseCaseInsensitive()
// date/time pattern
.appendPattern("dd-MMM-yy HH:mm:ss")
// use English locale for month name
.toFormatter(Locale.ENGLISH)
// set a timezone
.withZone(ZoneId.of("Europe/London"));
// parse the dates
ZonedDateTime z1 = ZonedDateTime.parse("01-SEP-17 10:00:00", fmt);
ZonedDateTime z2 = ZonedDateTime.parse("05-SEP-17 12:00:00", fmt);
// calculate the difference in hours
long diffHours = ChronoUnit.HOURS.between(z1, z2);
If you want to use UTC, just change the ZoneId to ZoneOffset.UTC constant. If you want to compare with the current date, just use:
// use the same ZoneId used in the formatter if you want to consider DST effects
ZonedDateTime.now(ZoneId.of("Europe/London"));
Conversions to/from Date
If you still need to work with java.util.Date, it's possible to convert from/to the new API. In Java 8 you can use native methods, and in Java <=7 the ThreeTen Backport has the org.threeten.bp.DateTimeUtils class.
To convert a Date to the new classes:
Date date = // java.util.Date
// convert to zoneddatetime (java 8)
ZonedDateTime z = date.toInstant().atZone(ZoneId.of("Europe/London"));
// convert to zoneddatetime (java 7 ThreeTen Backport)
ZonedDateTime z = DateTimeUtils.toInstant(date).atZone(ZoneId.of("Europe/London"));
To convert a ZonedDateTime back to a date:
// convert to zoneddatetime (java 8)
Date date = Date.from(z.toInstant());
// convert to zoneddatetime (java 7 ThreeTen Backport)
Date date = DateTimeUtils.toDate(z.toInstant());
You've essentially already got the times in milliseconds. You could always just compare the milliseconds directly instead.
long tenHoursInMillis = 36000000;
long dateVOMillis = someVO.getDate().getTime();
long dateSysMillis = System.currentTimeMillis();
if(dateSysMillis - dateAMillis > tenHoursInMillis) {
// do something
}
else if(dateSysMillis - dateAMillis < tenHoursInMillis) {
// do something else
}
// do something when they're equal

Number of days between two epoch days in scala

I have two epoch timestamps, I am trying to find the number of days between the two timestamps.
This is what I have now:
dateFrom = inputEntry.getValue(inputFields(0).get).asInstanceOf[String].toLong
dateTo =inputEntry.getValue(inputFields(1).get).asInstanceOf[String].toLong
Example:
dateFrom dateTo result
1501583232 1501641000 1
1501583232 1501986600 5
I am starting with two epoch dates here
tl;dr
ChronoUnit.DAYS.between( … , … )
Details
This has been covered many times already on Stack Overflow. So briefly here…
For date-time values, use date-time objects. Use java.time classes only, avoid the troublesome legacy date-time classes ( Date, Calendar, etc).
Do you mean a difference of dates or a difference of 24-hour chunks of time?
I'll go with dates here.
First, translate what appears to be number of whole seconds since an epoch reference date of 1970-01-01T00:00:00Z into a point in the timeline in UTC.
Note the L on the end of numeric literal to indicate a long rather than int.
Instant instant = Instant.ofEpochSecond( 1_501_583_232L ) ;
Assign time zone for which you want to consider dates.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z );
Convert to a date-only.
LocalDate ld = zdt.toLocalDate() ;
Get difference.
long days = ChronoUnit.DAYS.between( ld , ld2 ) ;
To get the results you want, you must define how you're going to calculate the difference.
Taking your first example (difference between 1501583232 and 1501641000 should be 1 day):
The epochs 1501583232 and 1501641000 are the number of seconds since 1970-01-01T00:00Z, so they are equivalent to the following UTC dates:
1501583232: 2017-08-01T10:27:12Z
1501641000: 2017-08-02T02:30:00Z
Note that the difference between them is 16 hours, 2 minutes and 48 seconds (so, less than a day). If you get the difference in days, technically it will be zero.
But if you consider only the dates (2017-08-01 and 2017-08-02) and ignore the time (hour/minute/second), then the difference can be either zero or 1, depending on the timezone you are.
If you consider only the UTC dates (2017-08-01 and 2017-08-02), the difference is 1 day.
But if you take the same UTC dates above in America/Los_Angeles timezone, you'll get:
1501583232: 2017-08-01T03:27:12-07:00
1501641000: 2017-08-01T19:30-07:00
Now the difference is zero days, no matter if you consider only the date (both are 2017-08-01), or the date and time (the difference in hours will be 16, less than a day).
So, you must define how you're going to calculate the difference (consider only the date, or both date and time, and what timezone will be used).
In your case, it seems that you're considering only the date and ignoring the time, but it's not clear what timezone it's using. Anyway, you can use JDK's 8 new java.time API for that (for JDK <= 7 you can use the ThreeTen Backport - 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).
The code is basically the same of #BasilBourque's answer, because it's very straighforward with the new API (I just wanted to add the insights above).
First you create the Instant's from the epoch values:
Instant instant1 = Instant.ofEpochSecond(1501583232L);
Instant instant2 = Instant.ofEpochSecond(1501641000L);
If you want the difference considering the date and time, you can use:
ChronoUnit.DAYS.between(instant1, instant2);
The result will be zero.
If you want to consider only the dates in UTC (and ignore the time), just do:
// convert to UTC and get just the date (day/month/year)
LocalDate d1 = instant1.atZone(ZoneOffset.UTC).toLocalDate();
LocalDate d2 = instant2.atZone(ZoneOffset.UTC).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
The result will be 1.
To convert to a different timezone (instead of UTC), use the ZoneId class:
// use a specific timezone
ZoneId zone = ZoneId.of("Asia/Kolkata");
// convert the Instant to a timezone and get only the date
LocalDate d1 = instant1.atZone(zone).toLocalDate();
LocalDate d2 = instant2.atZone(zone).toLocalDate();
long days = ChronoUnit.DAYS.between(d1, d2);
In this case, the difference is 1, but as I said above, different timezones can produce different results (can be either zero or 1 - for example, changing the code above to ZoneId.of("America/Los_Angeles"), the result is zero).
Note that the API uses IANA timezones names (always in the format Region/City, like Asia/Kolkata or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or IST) 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.

Jboss Java Date daylight saving time

Have an issue where, when clocks are moved due to a Daylight savings time (twice a year), dates are not correct in Java (I am based in Central Europe: GMT+2 in summer, GMT+1 in winter)
If time is moved 1 hour ahead, new Date() still returns old time (1 hour behind of current time).
In Java 7, can this be solved, without restarting the Jboss application servers?
If I change the time manually in Windows, reproduce the problem: Date is not updated to the system date unless jboss is restarted.
Calendar c = Calendar.getInstance();
c.setTime(new Date());
In Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes.
With this, you can handle DST changes easily.
First, you can use the org.threeten.bp.DateTimeUtils to convert from and to Calendar.
The following code converts the Calendar to org.threeten.bp.Instant, which is a class that represents an "UTC instant" (a timestamp independent of timezone: right now, at this moment, everybody in the world are in the same instant, although their local date and time might be different, depending on where they are).
Then, the Instant is converted to a org.threeten.bp.ZonedDateTime (which means: at this instant, what is the date and time at this timezone?). I also used the org.threeten.bp.ZoneId to get the timezone:
Calendar c = Calendar.getInstance();
c.setTime(new Date());
// get the current instant in UTC timestamp
Instant now = DateTimeUtils.toInstant(c);
// convert to some timezone
ZonedDateTime z = now.atZone(ZoneId.of("Europe/Berlin"));
// today is 08/06/2017, so Berlin is in DST (GMT+2)
System.out.println(z); // 2017-06-08T14:11:58.608+02:00[Europe/Berlin]
// testing with a date in January (not in DST, GMT+1)
System.out.println(z.withMonth(1)); // 2017-01-08T14:11:58.608+01:00[Europe/Berlin]
I've just picked some timezone that uses Central Europe timezone (Europe/Berlin): you can't use those 3-letter abbreviations, because they are ambiguous and not standard. You can change the code to the timezone that suits best for your system (you can get a list of all available timezones with ZoneId.getAvailableZoneIds()).
I prefer this solution because it's explicit what timezone we're using to display to the user (Date and Calendar's toString() methods use the default timezone behind the scenes and you never know what they're doing).
And internally, we can keep using the Instant, which is in UTC, so it's not affected by timezones (and you can always convert to and from timezones whenever you need) - if you want to convert the ZonedDateTime back to an Instant, just use the toInstant() method.
Actually, if you want to get the current date/time, just forget the old classes (Date and Calendar) and use just the Instant:
// get the current instant in UTC timestamp
Instant now = Instant.now();
But if you still need to use the old classes, just use DateTimeUtils to do the conversions.
The output of the examples above are the result of the ZonedDateTime.toString() method. If you want to change the format, use the org.threeten.bp.format.DateTimeFormatter class (take a look at the javadoc for more details about all the possible formats):
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss z X");
// DST (GMT+02)
System.out.println(formatter.format(z)); // 08/06/2017 14:11:58 CEST +02
// not DST (GMT+01)
System.out.println(formatter.format(z.withMonth(1))); // 08/01/2017 14:11:58 CET +01
Use ZonedDateTime class from JDK 8 java.time. It accommodates the Daylight Saving Time changes.
Refer the details at : https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html

UTC to Local date conversion

Hi I have a table from which I get a date like "2017-03-24 06:01:33" which is in UTC. In my java program I have to receive it as a string. How to convert it to a local date string using client's offset hour and minute?
I would do:
OffsetDateTime odt = LocalDateTime.parse(dateFromDb, DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"))
.atOffset(ZoneOffset.UTC);
String localDateString = odt.atZoneSameInstant(ZoneOffset.ofHoursMinutes(5, 30))
.toLocalDateTime()
.toString();
I am assuming you have got the client’s time zone as an offset in hours and minutes. Then you can pass those into from ZoneOffset.ofHoursMinutes(). Beware that if the client time zone uses summer time (daylight savings time) some of the year (as is the case in greater parts of the USA), you need to be sure you get the offset for the time of year where the date falls. Other ways of specifying the client time zone would be ZoneId.systemDefault() or ZoneId.of("Asia/Kolkata") — such would know the summer time rules and hence may be safer.
The code prints:
2017-03-24T11:31:33
You notice it’s five and a half hours ahead of the input string as expected.
If you only require the date, not the time, use toLocalDate() instead of toLocalDateTime().
All of the above requires Java 8. Or the backport of the Java 8 date and time classes to Java 6 and 7, see ThreeTen Backport. If you cannot use Java 8 and do not want to depend on the backport, find your inspiration in this question: Not able to parse UTC date time to EST local time.
Use this
simpleDateFormat.setTimeZone("Your-Client's-Timezone");

Categories

Resources