I need change every time zone of my DTO at runtime .
Today the time zone is informed by parameter when the User performs request on my web-service , I wonder if it is possible to apply the new time zone for all dates attributes .
The only thing I can not use is " TimeZone.setDefault ( myTimeZone ) " because that way apply to all JVM and how exists users of different time zones this solution is unfeasible .
I was trying something like this:
Query query = em.createNativeQuery(SQL.toString(), AgendamentoDTO.class);
collection = query.setParameter(1, idEmpresa).getResultList();
for (Field atributo : AgendamentoDTO.class.getDeclaredFields()) {
if (atributo.getType().isAssignableFrom(Date.class)) {
//Change time zone here
}
}
Tks
Avoid setting default time zone
As wisely advised in the Question, you should set the JVM’s current default time zone only as a last resort in the most desperate situation. Setting the default affects all code in all threads of all apps running within that JVM, and affects them immediately as they execute(!).
Instead, in all your date-time work, always pass the optional time zone argument to the various methods. Never rely implicitly on the JVM’s current default zone.
Avoid old date-time classes
The old legacy date-time classes bundled with the earliest versions of Java have proven to be poorly designed, troublesome, and confusing. Avoid them. Now supplanted by the java.time classes.
So instead of java.util.Date, use java.time.Instant. The Instant class represents a moment on the timeline in UTC with a resolution of nanoseconds. This Instant class is the basic building block of date-time handling. Use this class frequently, as much of your business logic, data storage, data exchange, and database work should all be in UTC. Do not think of UTC as just another variation on time zone, rather, think of UTC as the One True Time. While programming, forget about your own local time zone as that parochial thinking will confuse your programming.
Instant
Generally speaking, your web service should take and give UTC values. The Instant class can directly parse and generate strings to represent these values in standard ISO 8601 format.
Instant instant = Instant.parse( "2016-09-09T22:34:08Z" );
String output = instant.toString(); // Generates: 2016-09-09T22:34:08Z
So no need to manipulate these UTC values. Keep them around. A data-providing service should stick with UTC for the most part.
In your case the DTOs, being DTOs, should stick to storing UTC values (either Instant object or a string in ISO 8601 format in UTC with the Z on the end). By definition, a DTO should be ‘dumb’ in the sense of lacking business object and instead should be merely transporting basic data elements. The other objects consuming those DTOs should handle any needed time zone assignments.
ZonedDateTime
Generate strings in other time zones only for presentation to users. Here we assign a time zone of Québec to view the moment through the lens of a different wall-clock time. Apply a ZoneId to get a ZonedDateTime. The ZonedDateTime and the Instant both represent the very same moment in history, the same point on the timeline.
ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( z );
Notice that we are keeping the Instant object around in our business object, unmodified. We generate a distinct separate object, the ZonedDateTime, for a different wall-clock time.
When making these time zone assignments within your code, pass around ZoneId objects.
When specifying these time zone assignments through your web service API, pass the name of the time zone as a string. Always use proper IANA ‘tz’ time zone names in the format of continent/region such as America/Montreal or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST as they are not true time zones, not standardized, and not even unique(!).
Generating strings
When your web service is serving data to be consumed as data rather than presentation, generate strings in ISO 8601 format. The java.time classes use these standard formats by default for parsing and generating strings. Simply call toString to generate a string in standard format. Note that the ZonedDateTime extends the standard format by appending the name of the time zone in square brackets.
String output = instant.toString(); // 2016-09-09T22:34:08Z
String output = zdt.toString(); // 2016-09-09T19:34:08-03:00[America/Montreal]
When your web service is serving information for presentation to a user rather than for consumption as data, generate strings in a format appropriate to the user’s human language and cultural norms. You can specify a specific format. But generally best to let java.time automatically localize for you.
Locale locale = Locale.CANADA_FRENCH;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( l );
String output = zdt.format( f );
Related
I have a date as input = 2021-03-12T10:42:01.000Z.... and I want to transform into this format:
String pattern = "yyyy-MM-dd'T'HH:mm:ssZ";
public String getDate(XMLGregorianCalendar input) {
DateFormat f = new SimpleDateFormat(pattern);
input.toGregorianCalendar().setTimeZone(TimeZone.getTimeZone(ZoneOffset.UTC));
String output = f.format(input.toGregorianCalendar().getTime());
System.out.println(output);
}
2021-03-12T12:42:01+0200
Basically, it's adding 2hs more. Maybe it's related with the time zone, I didn't test it in another computer. I have 2 questions:
Why this is happening
What can I do to avoid it? It's a legacy app so I don't want to do a big change
Thanks
Basically, it's adding 2hs more
Not really. It's giving you the output for the same instant in time, but in your system local time zone - because you're creating a SimpleDateFormat without specifying a time zone (or a culture):
DateFormat f = new SimpleDateFormat(pattern);
Personally I'd recommend avoiding using java.text.SimpleDateFormat entirely, preferring the java.time types and formatters. But if you definitely want to use SimpleDateFormat, just make sure you set the time zone to UTC (assuming you always want UTC) and ideally set the culture as well (e.g. to Locale.ROOT).
The Answer by Jon Skeet is correct, and smart. You appear to be seeing simply a time zone adjustment. Your two strings 2021-03-12T10:42:01.000Z & 2021-03-12T12:42:01+0200 represent the very same moment. The 12 noon hour, if two hours ahead of UTC, is the same as 10 AM hour with an offset-from-UTC of zero hours-minutes-seconds.
And, as mentioned in that other Answer, you really should avoid using the terrible date-time classes bundled with the earliest versions of Java.
tl;dr
myXMLGregorianCalendar // Legacy class, representing a moment as seen in some time zone.
.toGregorianCalendar() // Another legacy class, also representing a moment as seen in some time zone.
.toZonedDateTime() // A modern *java.time* class, representing a moment as seen in some time zone.
.toInstant() // Another *java.time* class, for representing a moment as seen in UTC.
.truncatedTo( // Lopping off some smaller part of the date-time value.
ChronoUnit.SECONDS // Specifying whole seconds as our granularity of truncation, so lopping off any fractional second.
) // Returns another `Instant` object, rather than altering (mutating) the original, per immutable objects pattern.
.toString() // Generating text representing the content of our `Instant` object, using standard ISO 8601 format.
java.time
The modern approach uses the java.time classes that years ago supplanted SimpleDateFormat, XMLGregorianCalendar , GregorianCalendar, and such.
Convert legacy <——> modern
You can easily convert from the legacy types to java.time. Look for new to/from methods on the old classes.
ZonedDateTime zdt = myXMLGregorianCalendar.toGregorianCalendar().toZonedDateTime() ;
Adjust to offset of zero
Adjust from whatever time zone to UTC by extracting an Instant. This class represents a moment as seen in UTC, always in UTC.
Instant instant = zdt.toInstant() ;
Understand that zdt and instant both represent the same moment, the same point on the timeline, but differ in their wall-clock time.
Truncation
Given the formatting pattern seen in your Question, you seem want to work with a granularity of whole seconds. To lop off any fractional second, truncate to seconds.
Instant truncated = instant.truncatedTo( ChronoUnit.SECONDS ) ;
ISO 8601
Your desired text format is defined in the ISO 8601 standard. That standard is used by default in java.time for parsing/generating strings. So no need to specify any formatting pattern.
String output = truncated.toString() ;
If we have timestamps that contain the timezone info, like 2017-07-03T17:30:00-04:00, and parse it into java.Date or joda.DateTime.
Does it contains timezone information ?
I am asking this because i want to compare two different date instance. So if it does not contain timezone information, the day difference will be wrong with different timezones
UPDATE:
I run a quick unit test to verify, first convert date instance to milliseconds and convert back to TimeUnit after subtract these two milliseconds. The hours are different for different timezone
Both java.util.Date and Joda-Time have been supplanted by the java.time classes.
Your input string 2017-07-03T17:30:00-04:00 is in standard ISO 8601 format and has an offset-from-UTC at the end. That -04:00 means the string represents a moment four hours behind UTC.
This offset is not a time zone. A time zone is a history of offsets for a particular region. For example, America/Barbados or America/New_York.
Parse your string as an java.time.OffsetDateTime object.
OffsetDateTime odt = OffsetDateTime.parse( "2017-07-03T17:30:00-04:00" );
odt.toString(): 2017-07-03T17:30:00-04:00
You may compare OffsetDateTime instances by calling the methods IsEqual, isBefore, and isAfter.
To see the same simultaneous moment in UTC, extract an Instant.
Instant instant = odt.toInstant() ;
instant.toString(): 2017-07-03T21:30:00Z
The Z on the end is short for Zulu and means UTC.
It is going to depend on what type of DateTime you use, as of Java 8 you have these options:
A LocalDate or LocalDateTime. It is going to discard time zone information, you will wind up with a value that is 'valid' only for the local timezone. This value is ambiguous without some context as to the specific timezone of the server process which generated the value.
A ZonedDate or ZonedDateTime. This one preserves the time zone. Comparison is still going to be ambiguous: you have issues like DST or calendaring changes to contend with (depending on the range of datetime which you need to be compatible with). For sorting/comparison purposes you would probably want to convert it to a reference timescale, which is why:
An Instant represents a particular moment in time, on the absolute timescale of UTC. Any Instant is directly comparable with any other Instant and any ambiguity in values is resolved by the definition of Instant. Input values will be converted to the matching counterparts in UTC, so the original timezone (if any) will be lost even if the absolute time value will be preserved correctly. Instant is therefore not a good choice if you rely on the timezone to make decisions about location or locale, for instance.
I have an ec2 instance running in Singapore zone. By default time zone for this instance is UTC. So I've set it to IST assuming, application which is running in this instance generates time in IST which we store in the database.
i.e new Date() in my application shoud return time in IST. But it's still generating time in UTC which isn't we needed.
So, how to make new Date() gives time in IST in ec2 instance without adding explicit java code?
Try to set -Duser.timezone="Asia/India" as Environment Variable or run your java app with that switch from terminal using java filename -Duser.timezone="Asia/India"
java.time
ZonedDateTime.now(
ZoneId.of( "Asia/Kolkata" )
)
Details
The toString method of Date confusingly applies the JVM’s current default time zone while generating the string. This misleads you into thinking it has an assigned time zone but does not^. The Date object actually represents a value in UTC. One of many reasons to avoid these troublesome old legacy date-time classes.
This class is now supplanted by the java.time classes.
The Instant class represents a moment in UTC.
Instant instant = Instant.now();
Adjust into your desired time zone. Never use 3-4 letter abbreviations such as IST. Not a real time zone. Use real time zone names.
ZoneId z = ZoneId.of( "Asia/Kolkata" );
ZonedDateTime zdt = instant.atZone( z );
This Question is really a duplicate of many others. Search Stack Overflow for much more discussions and examples.
Do not alter the time zone of your host operating system nor the JVM’s default time zone. You simply should not rely on the default time zone as it can be changed at any moment at runtime by any code in any thread of any app within your JVM. Always specify the optional argument for time zone when calling the java.time classes, problem solved.
^To make things even more confusing, there actually is a time zone within the Date class, but without getters or setters. Used internally, but irrelevant to our discussion here. These old legacy date-time classes are a very confusing mess.
I am trying to convert the ZonedDateTime to a Date. Looks like in the conversion, it looses the time zone and gets my local time.
System.out.println("zoneDate1::::::::::"+ZonedDateTime.now(ZoneId.of("America/Chicago")));
System.out.println("zoneDate1:Date::::::::::"+Date.from(ZonedDateTime.now(ZoneId.of("America/Chicago")).toInstant()));
The above outputs as below:
zoneDate1::::::::::2016-04-15T17:35:06.357-05:00[America/Chicago]
zoneDate1:Date::::::::::Fri Apr 15 18:35:06 EDT 2016
Is this because this is a Date type? How would i go about doing this kind of conversion and conserve the zoned time?
What is the problem? What did you expect? I see no misbehavior.
Your java.time type (ZonedDateTime) is assigned a time zone of America/Chicago.
Your JVM apparently has an assigned time zone related to east coast of North America, the clue being the EDT value seen in string output. The toString method on java.util.Date applies your JVM’s current default time zone when generating its textual representation of the date-time value. Poorly designed, this behavior is trying to be helpful but is actually confusing because you cannot actually get or set this time zone on the java.util.Date object.
At any rate, the east coast of North America (such as America/New_York time zone) is an hour ahead of America/Chicago. So you are seeing 17:xx:xx time for Chicago and 18:xx:xx for Eastern Daylight Saving Time. These values are correct.
You should call java.util.TimeZone.getDefault() when investigating the behavior of the old date-time classes.
java.time
The bigger problem is that you are even using these old date-time classes such as java.util.Date/.Calendar. They are poorly designed, confusing, and troublesome. Avoid these old classes altogether. They have been supplanted in Java 8 and later by the java.time framework.
Also, avoid using 3-4 letter zone abbreviations like EDT. These are neither standardized nor unique. Use proper time zone names in continent/region format.
Instant
To capture the current date-time in java.time, just use Instant. This class captures a moment on the timeline in UTC. Do most of your work in UTC. No need for time zones unless expected by your user when displayed in the user interface.
Instant now = Instant.now();
Database
To send to your database, first make sure you have defined the column in the table as something along the line of the SQL standard TIMESTAMP WITH TIME ZONE. By the way, support for date-time types various among databases with some doing a much better job than others.
Hopefully JDBC drivers will be updated someday to directly handle the java.time types. Until then, we must convert into a java.sql type when transferring data to/from a database. The old java.sql classes have new methods to facilitate these conversions.
java.sql.Timestamp
For a date-time value like Instant, we need the java.sql.Timestamp class and its from( Instant ) method.
java.sql.Timestamp ts = java.sql.Timestamp.from( now );
Avoid working in java.sql.Timestamp as it is part of the old poorly-designed mess that is the early Java date-time classes. Use them only for database transfer, then shift into java.time immediately.
Instant instant = ts.toInstant();
So simple, no time zones or offset-from-UTC involved. The Instant, java.sql.Timestamp, and database storage are all in UTC.
ZonedDateTime
When you do need to shift into some locality’s wall-clock time, apply a time zone.
ZoneId zoneId = ZoneId.of( "America/Chicago" ); // Or "America/New_York" and so on.
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Huh? Date doesn't have time zones so, this is probably why it's failing. Maybe this is what you're looking for:
Date.from(java.time.ZonedDateTime.now().toInstant());
If your database allows you to store the timestamp along with the timezone, you should go ahead and save it as a timestamp.
If not, I would recommend that you store the date-time as per your timezone (or GMT). Add an additional column in the table to hold the value of the user's timezone.
When you fetch the value from the database, you can convert it to the user's timezone. Avoid storing just the date.
I've written some Java software that very frequently persists and retrieves Joda-Time DateTime objects from Redis. I just serialise and deserialise the objects at present. The software reads the objects about 50 times more often than it writes. I've not profiled serialising/deserialising Joda-Time objects, but the software has scaled well, computationally, under load and I'm happy with the performance.
What hasn't scaled well is memory usage. The serialised Joda-Time objects are pretty big and a decent size Redis instance can only take about 3 days worth of customer data before I need to flush it out to a relational database on disk. A secondary issue is Redis' own backup mechanisms seem harder to manage the larger the dataset gets...
Setting aside the temptation to throw more RAM at the problem, I've thought of the following ideas so far:
serialise then compress the objects before persisting
persist as a ISO date format string
persist as some other Joda-compatible string format
I will try out and profile these before deciding, but I wonder if anyone can think of a more efficient way of reducing the memory footprint of persisted Joda objects without breaking the computational bank?
ISO 8601
While I know nothing of Redis… Generally speaking, the easiest and most efficient way to serialize Joda-Time objects is to take advantage of their built-in support for the sensible, unambiguous, standard ISO 8601 string formats for date-time values.
For a zoned date-time value, the standard provides a YYYY-MM-DDTHH:MM:SS.SSS±HH:SS format such as 2014-10-24T21:17:30+02:00 or 2014-10-24T19:17:30Z (Z for Zulu means an offset of 00:00 from UTC).
The various Joda-Time 2.5 classes use ISO 8601 as their defaults for parsing and generating String representations of date-time values.
Generating Strings
For DateTime, simply call its toString method either explicitly or implicitly.
String output = DateTime.now( DateTimeZone.forID( "America/Montreal" ) ).toString();
Generally best to work with UTC when storing date-time values. Joda-Time lets you easily adjust to UTC.
DateTime nowMontreal = DateTime.now( DateTimeZone.forID( "America/Montreal" ) );
DateTime nowUtc = nowMontreal.withZone( DateTimeZone.UTC );
String output = nowUtc.toString();
Another example.
DateTime output = DateTime.now( DateTimeZone.UTC ).toString();
Parsing Strings
Parsing is just as easy. The only issue is time zone. If you omit a time zone, generally Joda-Time will assign the JVM’s current default time zone. Usually better if you explicitly specify the desired time zone.
DateTime dateTimeMontreal = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.forID( "America/Montreal" ) );
or, for UTC…
DateTime dateTimeUtc = new DateTime( "2014-10-24T19:17:30Z", DateTimeZone.UTC ) );
java.time
Another alternative is the new java.time package built into Java 8. Inspired by Joda-Time, java.time is similar in many ways. But one difference is that java.time by default generates string representations by extending the ISO 8601 standard to append the name of the time zone. While standard format has an offset-from-UTC, you loose the actual time zone information. (A time zone is an offset plus the rules for Daylight Saving Time and other anomalies in the present, future, and past.)
On the other hand, generally it is best to store date-time in UTC. If you really care about the time zone at the time of data-entry, it’s generally best to store that information separately in addition to the UTC-adjusted value.
In java.time, the Instant class represents a moment on the timeline in UTC.
Instant instant = Instant.parse( "2014-10-24T19:17:30Z" );
String outputInstant = instant.toString();
2014-10-24T19:17:30Z
To adjust into a time zone, specify a ZoneId to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
String outputZdt = zdt.toString();
2014-10-24T15:17:30-04:00[America/Montreal]
Try to analyze a distribution of your date-time objects. If it happens that they are relatively close to each other, then you can do some "magic":
1) you can introduce a special "starting point date" constant and then store the actual date as a number of days shift from the constant - that would be integer value (~8 bytes on 64bit arch. w/o compression)
2) do you need actual time? if no - just throw away time; if yes - you can store hours+minutes+seconds in one int variable (another ~8 bytes on 64bit arch. w/o compression)
3) analyze results - there is a chance that you could fit both: the date (shift) and the time in a single int variable
4) introduce a caching mechanism, that would greatly increase performance of serializing/deserializing your objects
Store the millis since the start of the epoch. It is a single long value. If you need a timezoned value also store timezone Id as a string. Serializing and parsing the string representation will always take more resources including RAM, there so much data processing inside, some regex, split calls which allocates more memory.
Use this constructor for restoring the value: public BaseDateTime(long instant, DateTimeZone zone)
It is so light waight because it is stores rightaway what is under the hood of every DateTime instance:
public BaseDateTime(long instant, Chronology chronology) {
super();
iChronology = checkChronology(chronology);
iMillis = checkInstant(instant, iChronology);
adjustForMinMax();
}