Let's say I have a Java "Date" object. I would like to convert/format it to the current time zone of my Spring Boot/Alfresco service application.
How can I achieve this ? I know Alfresco does it to display the date in formularies.
For example, I have a date property set to 3 pm (UTC), the system will display "17:00:00" (5pm, french time).
I would like to get the same result but with Java.
Any idea ?
Thank you!
If you could use java.time (Java 8+ needed), you could get the default zone id of a system with
ZoneId sysDefault = ZoneId.systemDefault();
You can get the current time with the speaking method now(), in this case use a ZonedDateTime.now() for the time in the system default zone or apply a zone to now(ZoneId) if you want the current time in a specific zone.
Having a zone and/or a ZonedDateTime, you can safely switch between zones, the conversion will be handled by the class(es). You can use DateTimeFormatters in order to alter display styles, like 24h or 12h format, double-digit units or single-digit ones and so on.
Here's a simple example for switching a datetime from UTC to Paris zone. The zones in this example are fixed, but you can use the system default zone id of your server instead of the Paris zone.
public static void main(String[] args) {
// define the zones needed for conversion, one for UTC
ZoneId utc = ZoneId.of("UTC");
// and a second one for Paris
ZoneId paris = ZoneId.of("Europe/Paris");
// create date and time of day in utc
ZonedDateTime threePmUtc = ZonedDateTime.of(2022, 8, 5, 15, 0, 0, 0, utc);
// display its time of day once
System.out.println(threePmUtc.toLocalTime()
.format(DateTimeFormatter.ISO_LOCAL_TIME));
// then convert to Paris time
ZonedDateTime fivePmParis = threePmUtc.withZoneSameInstant(paris);
// display its time of day
System.out.println(fivePmParis.toLocalTime()
.format(DateTimeFormatter.ISO_LOCAL_TIME));
/*
* if you want more control over the output,
* e.g. don't print seconds if they are zero or switch to 12h format,
* then build a formatter with your desired format
*/
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("h.mm a", Locale.ENGLISH);
// and print the formatted value
System.out.println(fivePmParis.toLocalTime()
.format(dtf));
}
Output:
15:00:00
17:00:00
5.00 PM
Related
I have a Date with the actual time of my system (I live in Spain). I need to change it to UTC-1, but it doesn't matter if I write "UTC-1" or "UTC-2", it always gives me the same time less 2 hours, I mean:
My system hour (time_utc): 11:00 13/04/2021
Try UTC-1 (time): 09:00 13/04/21
Try UTC-2 (time): 09:00 13/04/21
I have this code:
Date time_utc = new Date();
DateFormat convertidor = new SimpleDateFormat("yyyy-MM-dd HH:00:00.000");
convertidor.setTimeZone(TimeZone.getTimeZone("UTC-1"));
time = convertidor.format(time_utc);
Why it doesn't work? Can anyone helps me? Thanks a lot!
¡Hola!
You can do that in a pretty short way using java.time (if you are allowed and willing to do so).
There are special classes that represent a moment in time in different time zones of offsets. One of them is an OffsetDateTime, see this example:
public class Main {
public static void main(String[] args) {
// create one of your example date times in UTC
OffsetDateTime utcOdt = OffsetDateTime.of(2021, 4, 13, 11, 0, 0, 0, ZoneOffset.UTC);
// and print it
System.out.println(utcOdt);
/*
* then create another OffsetDateTime
* representing the very same instant in a different offset
*/
OffsetDateTime utcPlusTwoOdt = utcOdt.withOffsetSameInstant(ZoneOffset.ofHours(2));
// and print it
System.out.println(utcPlusTwoOdt);
// do that again to see "the other side" of UTC (minus one hour)
OffsetDateTime utcMinusOneOdt = utcOdt.withOffsetSameInstant(ZoneOffset.ofHours(-1));
// and print that, too.
System.out.println(utcMinusOneOdt);
}
}
It outputs the following three lines:
2021-04-13T11:00Z
2021-04-13T13:00+02:00
2021-04-13T10:00-01:00
As you can see, the time of day is adjusted according to the offset.
The output could be formatted in your desired style if needed (this currently just uses the toString() method of OffsetDateTime).
UPDATE
You can achieve the output formatted as desired by defining the pattern as uuuu-MM-dd HH:mm when using a java.time.format.DateTimeFormatter.
Just add the following lines to the example above:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
System.out.println(utcOdt.format(dtf));
System.out.println(utcPlusTwoOdt.format(dtf));
System.out.println(utcMinusOneOdt.format(dtf));
This would then output
2021-04-13 11:00
2021-04-13 13:00
2021-04-13 10:00
And if you really want fix zeros for seconds and millis, then create your DateTimeFormatter like this:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:00.000");
which will cause output like this:
2021-04-13 11:00:00.000
2021-04-13 13:00:00.000
2021-04-13 10:00:00.000
As a supplement to the good answer by deHaar:
As Matt Johnson-Pint already asked, do you need to convert to a different time zone? This would be the most typical. If so, use that time zone, not just a UTC offset of -1. By all probability that time zone has used other offsets in the past and may well do so in the future. So -01:00 isn’t safe. A real time zone ID like Atlantic/Cape_Verde is safer.
You don’t need to go through the current time in your own time zone and convert. java.time can directly give you the current time in another time zone or at a specific UTC offset.
java.time can also truncate a time to whole hours.
So for example:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
ZoneId zone = ZoneId.of("Atlantic/Cape_Verde");
ZonedDateTime nowInCaboVerde = ZonedDateTime.now(zone);
System.out.println(nowInCaboVerde);
System.out.println(nowInCaboVerde.truncatedTo(ChronoUnit.HOURS)
.format(formatter));
Output:
2021-04-14T03:12:28.272010-01:00[Atlantic/Cape_Verde]
2021-04-14 03:00:00.000
PS Cabo Verde/Cape Verde was at offset -02:00 until 1975.
What went wrong in your code?
This is how confusingly the old TimeZone class behaves and one of the reasons why you should never use it: When given a time zone ID that it does not recognize, it returns GMT and pretends all is well. UTC-1 is not a recognized time zone ID. In case it didn’t make sense to refer to a real time zone and you needed the offset -01:00 from UTC, you might have used GMT-1 or GMT-01:00. Yes, TimeZone refers to UTC as GMT even though they are not strictly speaking the same.
I have a problem with converting dates between Instant and LocalDate and back. I need to change my date to monday in its current week (if it's wednesday then I'm changing to monday):
public static Instant getStartDateAsMonday(Instant startTime) {
LocalDate monday = startTime.atZone(ZoneId.systemDefault()).toLocalDate()
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday.atStartOfDay(ZoneId.systemDefault()).toInstant();
return startTime;
}
When I test it ...
#Test
public void testGetStartDateAsMonday() {
Instant instant = DateHelperService.getStartDateAsBeginningOnMonday(
Instant.parse("2020-05-27T00:00:00Z"));
assertThat(instant).isEqualTo("2020-05-25T00:00:00Z");
}
... the test doesn't pass and gives the output:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
My system default timezone is GMT+2. The test passes when I do atStartOfDay(ZoneId.of("UTC")), but I don't understand why I can't use my system default in that conversion.
Firstly, calling ZoneId.systemDefault() twice is bad practice. The JVM’s current default time zone may have changed between calls. Any code in any thread of any app within that JVM can change the current default time zone with immediate effect.
So, capture the current default.
ZoneId z = ZoneId.systemDefault() ;
You said:
My system default timezone is GMT+2
GMT+2 represents an offset not a time zone. An offset is merely a number of hours-minutes-seconds, positive or negative. A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region. A time zone has a name in the format of Continent/Region.
You said:
Expected : 2020-05-25T00:00:00Z
Actual : 2020-05-24T22:00:00Z
Your expectation is incorrect. If you have a moment representing the first moment of the day in a zone such as Europe/Brussels or Africa/Cairo for a time-of-day of 00:00, and you know that zone runs two hours ahead of UTC on that date, then intuitively you know UTC is two hours earlier. If the clock strikes midnight in Brussels or Cairo, you know the clock cannot yet be striking midnight in UTC, at the Greenwich Royal Observatory, or in Iceland. Midnight won’t strike there for two more hours. So the date is still “yesterday” in UTC. And two hours earlier than 00:00 is 22:00. So your actual result is correct.
Let's go through it method by method:
LocalDate monday = startTime // startTime is 2020-05-27 00:00:00 as an Instant.
// 2020-05-27 02:00:00, but with GMT+2 information as it is a ZonedDateTime now.
.atZone(ZoneId.systemDefault())
// (1) 2020-05-27 02:00:00 without any zone information.
// (2) 2020-05-27 00:00:00 because hour is dropped as it's a LocalDate here.
.toLocalDate()
// Changed to 2020-05-25 00:00:00, which is what you want.
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
startTime = monday // monday is 2020-05-25 00:00:00 as LocalDate.
// You say that this is at GMT+2 and you want the start of day.
// You get a ZonedDateTime 2020-05-25 00:00:00 with information about GMT+2.
.atStartOfDay(ZoneId.systemDefault())
// Instant has no zone offset, so this is what the ZonedDateTime needs to consider.
// 2020-05-25 00:00:00 becomes 2020-05-24 22:00:00 as the +2 offset is subtracted.
.toInstant();
When you are using ZoneId.of("UTC") no problems will happen as the offset of ZoneId.of("UTC") is zero (daylight saving stuff ignored for simplicity). A datetime minus zero hours is the same datetime.
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);
I am creating date like this:
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
Date.from(now.toInstant());
I need Date object have current time in utc, but when I print date it gives me my local time and not utc time.
I also tried with:
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
Date date = Date.from(now.toInstant());
But when I print Date again time is not in utc. Am I doing something wrong when creating Date object. Why above 2 approaches not give me Date that have current time in utc.
Two points:
Avoid the long outdated Date class, in particular when you are already using classes from java.time, the modern Java date and time API.
A Date object hasn’t got and cannot have a time zone in it.
To print offset or time zone
If you need your offset, you need to hold on to your OffsetDateTime (or ZonedDateTime) object:
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(now);
On my computer this just printed
2017-11-21T11:53:11.519Z
The Z in the end indicates Zulu time zone, another name for UTC (you may also informally think of it as Zero offset from UTC).
If you would like a more human-readable format, you are right, use a formatter:
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL);
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
System.out.println(now.format(formatter));
Depending on your locale and the time, this prints something like
Tuesday, November 21, 2017 11:53:11 AM Z
Again the Z means Zulu time zone, UTC.
Date is not going to help you
A Date is just a point in time. So is the Instant that you use for initializing the date. None of them has got a time zone or offset. The difference here is their toString methods: The Instant is always printed in UTC, the Date usually (always?) in the JVM’s default time zone. The latter confuses many into thinking the Date has a time zone when it hasn’t. See All about java.util.Date.
As I have demonstrated, a formatter may put a time zone or offset into a string when formatting the date-time. This does not in any way modify the date-time object, whether OffsetDateTime, ZonedDateTime, Instant or Date. The long outdated DateFormat class may do the same when formatting a Date. It cannot and will not set a time zone in the Date object since (and I repeat) a Date object cannot have a time zone in it.
Long story short, you have no need for the outdated Date class that I can see.
When I try to print the current date and time using Calender instance, the result I get is 1 hour ahead from the actual time.
I am working in remote machine which runs with EST time zone. Below is what I tried, but nothing works.
Calendar cal = Calendar.getInstance(TimeZone.getDefault());
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
System.out.println("Current Date & Time: " +calendar.getTime());
o/p:
Time: Sat Dec 28 11:55:10 UTC 2013
But expected o/p:
Time: Sat Dec 28 10:55:10 UTC 2013
All the 3 types give same result. I couldn't understand what I miss out to get the exact date & time. Is this problem related to daylight time saving ?
Could someone help me to overcome this problem. Thanks in Advance.
It is again this old pitfall with java.util.Date: Its toString()-method which you indirectly use when printing calendar.getTime() uses your default time zone, not the time zone of your calendar instance (which you set to 'EST').
Solution:
Date currentTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("Current Date & Time: " + sdf.format(currentTime));
Explanations:
a) In first line no calendar instance is necessary because you are just interested in current global time (the same physical time independent from timezone). Calendar.getInstance() is also more consuming resources. Finally, both expressions new Date() and Calendar.getInstance(...).getTime() have no time zone reference when it is about the internal state. Only the toString()-method of j.u.Date uses default time zone.
b) You need to define an output format which is given in line 2. It is up to you to change it. Just study the pattern documentation of java.text.SimpleDateFormat.
c) You also need to define the time zone in your output format to help the format object to translate the global Date-instance into a timezone-aware representation. By the way, I had choosen the identifier 'America/New_York', not 'EST' because latter form can be sometimes ambigous. You should either choose the first form (IANA- or Olson time zone identifier) or the form 'GMT+/-HH:mm'.
d) The output itself is done with sdf.format(currentTime), not just currentTime (no implicit call of toString()).
e) To answer your question 'Is this problem related to daylight time saving ?': No, the time in time zone EST (America/New_York) is never in DST in december.
Conclusion:
If you can, you should try to avoid j.u.Date and j.u.Calendar because there are too many pitfalls. At the moment JodaTime is a better alternative, although not without issues.
Avoid 3-Letter Time Zone
Never use the 3-letter time zone codes. They are neither standardized nor unique. Your "EST" can mean at least these:
Eastern Standard Time (USA)
Eastern Standard Time (Australia)
Eastern Brazil Standard Time
Use time zone names.
Avoid j.u.Date/Calendar
You have discovered one of the many reasons to avoid using java.util.Date & java.util.Calendar classes bundled with Java: A Date instance has no time zone information yet its toString method confusingly renders a string based on your Java environment's default time zone.
Use Joda-Time
Use a competent date-time library. In Java that means either Joda-Time, or in Java 8, the new java.time.* classes (inspired by Joda-Time).
Example code…
// Default time zone
DateTime dateTime_MyDefaultTimeZone = new DateTime();
// Specific time zone. If by "EST" you meant east coast of United States, use a name such as New York.
DateTimeZone timeZone = DateTimeZone.forID( "America/New_York" );
DateTime dateTime_EastCoastUS = new DateTime( timeZone );
Dump to console…
System.out.println( "dateTime_MyDefaultTimeZone: " + dateTime_MyDefaultTimeZone );
System.out.println( "dateTime_EastCoastUS: " + dateTime_EastCoastUS );
System.out.println( "date-time in UTC: " + dateTime_EastCoastUS.toDateTime( DateTimeZone.UTC ) );
When run…
dateTime_MyDefaultTimeZone: 2013-12-28T18:51:18.485-08:00
dateTime_EastCoastUS: 2013-12-28T21:51:18.522-05:00
date-time in UTC: 2013-12-29T02:51:18.522Z