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();
}
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() ;
I have string of the pattern "yyyy-MM-dd'T'HH:mm:ssZ" which I want to convert to ZonedDateTime format using Java.
Input String Example: "2019-11-23T10:32:15+12:24"
Output: ZonedDateTime
Edit: I have tried this but it does not work.
ZonedDateTime convertToZonedDateTime(final String source) {
final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = dateFormat.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
return ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}
I have this solution which works for string "2018-04-05 19:58:55" produces output 2018-04-05T19:58:55+05:30[Asia/Kolkata] but when I change the pattern in function to "yyyy-MM-dd'T'HH:mm:ssZ" and input string to 2019-11-23T10:32:15+12:24 it does not work due to ParseException: Unparsable data.
I need ZonedDateTime format for an API which expects the input time in that format.
tl;dr
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
Semantically, we are done at this point with a OffsetDateTime object in hand.
But you claim to be using an API that demands a ZoneDateTime object. We have no known time zone to apply, so let’s apply UTC (an offset of zero hours-minutes-seconds).
OffsetDateTime // Represent a moment as a date with time-of-day in the context of an offset-from-UTC (a number of hours-minutes-seconds).
.parse( // Parse text into a date-time object.
"2019-11-23T10:32:15+12:24" // The offset of +12:24 looks suspicious, likely an error.
) // Returns an `OffsetDateTime` object.
.atZoneSameInstant( // Convert from `OffsetDateTime` to `ZonedDateTime` by applying a time zone.
ZoneOffset.UTC // This constant is a `ZoneOffset` object, whose class extends from `ZoneId`. So we can use it as a time zone, though semantically we are making a mess.
) // Returns a `ZonedDateTime` object.
.toString() // Generate text in standard ISO 8601 format.
See this code run live at IdeOne.com.
2019-11-22T22:08:15Z
Caveat: The offset on your example input string looks wrong to me.
Details
You need to understand some concepts for date-time handling.
Offset
A offset-from-UTC is merely a number of hours-minutes-seconds ahead of, or behind, the meridian line drawn at the Greenwich Royal Observatory.
In Java, we represent an offset with the ZoneOffset class. A date and time-of-day in the context of an offset is represented with the OffsetDateTime class. Such an object represents a moment, a specific point on the timeline.
Time zone
A time zone is much more. A time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. These changes are determined by politicians. So these changes can be arbitrary and capricious, and happen surprisingly often, often with little or no warning. In North America, for example, most regions have adopted Daylight Saving Time (DST) nonsense, resulting in the offset changing twice a year. Currently there is a fad amongst politicians to quit DST changes while staying permanently year-round on “summer time”, one hour ahead of standard time.
There is a database cataloging these changes. The tZ data is a file maintained by IANA listing changes worldwide. You’ll likely find copies of this data in your host OS, in enterprise-quality database management systems such as Postgres, and in your Java Virtual Machine. Be sure to keep these up-to-date with changes in zones you care about.
Time zones have names in the format of Continent/Region. For example, Africa/Tunis, Europe/Paris, and Asia/Kolkata.
OffsetDateTime
So an input string like "2019-11-23T10:32:15+12:24" has no indicator of time zone, only an offset. So we must parse it an a OffsetDateTime.
OffsetDateTime odt = OffsetDateTime.parse( "2019-11-23T10:32:15+12:24" ) ;
Asking for that as a ZonedDateTime makes no sense. We cannot reliably determine a time zone merely from an offset. Many time zones may share an offset for some pints in time.
Also, that particular input string 2019-11-23T10:32:15+12:24 is suspect. That offset of twelve hours and twenty-four minutes does not map to any current time zone. Are you sure it is correct?
You can convert your OffsetDateTime to a ZonedDateTime by specifying a time zone to use in adjustment. I suggest using UTC. While this works technically, semantically it is confusing. Moments in UTC are best represented by OffsetDateTime rather than ZonedDateTime. But apparently you are interoperating with code that demands a ZonedDateTime specifically, so c’est la vie.
ZonedDateTime zdt = odt.atZoneSameInstant( ZoneOffset.UTC ) ;
Instant
Tip: Generally, APIs should be written to hand off moments as an Instant object, which is always in UTC by definition.
LocalDateTime
You present another string input, "2018-04-05 19:58:55". This input lacks any indicator of time zone or offset-from-UTC. So we cannot know if this means almost-8PM in Tokyo Japan, or almost-8PM in Toulouse France, or almost-8PM in Toledo Ohio US — which are all events happening several hours apart, different points on the time zone.
Such a value must be parsed as a LocalDateTime. Replace the SPACE in the middle with a T to comply with ISO 8601 standard formatting.
LocalDateTime ldt = LocalDateTime.parse( "2018-04-05 19:58:55".replace( " " , "T" ) ) ;
The resulting object does not represent a moment, is not a point in the timeline. Such an object represents potential moments along a spectrum of about 26-27 hours, the range of time zones around the globe.
ZonedDateTime
If you are certain that input string was intended for a particular time zone, apply a ZoneId to get a ZonedDateTime. Then you have determined a moment, a specific point on the timeline.
ZoneId z = ZonedId.of( "Asia/Kolkata" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
You can parse the input date time string into OffsetDateTime and then convert it into ZonedDateTime
String inputDate = "2019-11-23T10:32:15+12:24";
OffsetDateTime offset = OffsetDateTime.parse(inputDate);
ZonedDateTime dateTime = offset.toZonedDateTime();
If you just need ZonedDateTime at same local time with ZoneId then use atZoneSimilarLocal
ZonedDateTime dateTime = offset.atZoneSimilarLocal(ZoneId.systemDefault());
It’s unclear why you think you want a ZonedDateTime, and if you do, in which time zone. A bit of the following has been said already, but I would like to give you three suggestions to choose from:
You don’t need a ZonedDateTime. An OffsetDateTime fits your string better.
If you want a ZonedDateTime in your default time zone, which makes sense, use OffsetDateTime.atZoneSameInstant() (as in the answer by Basil Bourque).
If you just want a ZonedDateTime representation of your string, the one-arg ZonedDateTime.parse() parses it directly.
Use OffsetDateTime
Your string contains an offset, +12:34, and not a time zone, like Pacific/Galapagos. So OffsetDateTime is more correct to represent its content.
String inputStringExample = "2019-11-23T10:32:15+12:24";
OffsetDateTime dateTime = OffsetDateTime.parse(inputStringExample);
System.out.println(dateTime);
Output from this snippet is:
2019-11-23T10:32:15+12:24
I agree with the comment by Basil Bourque, the offset of +12:24 doesn’t look like a real-world UTC offset, but it’s fine for a Stack Overflow example. In 2019 most offsets are on a whole hour and the rest generally on a whole quarter of an hour, so 24 minutes is not used. Historic offsets include many with both minutes and seconds.
I am exploiting the fact that your string is in ISO 8601 format. The classes of java.time parse the most common ISO 8601 variants as their default, that is, without any explicit formatter. Which is good because writing a format pattern string is always error-prone.
Use OffsetDateTime.atZoneSameInstant()
Your call to ZoneId.systemDefault() in the code in the question seems to suggest that you want a ZonedDateTime in your default time zone. On one hand this use of ZonedDateTime seems reasonable and sound. On the other hand relying on ZoneId.systemDefault() is shaky since the default time zone of your JVM can be changed at any time by another part of your program or any ther program running in the same JVM.
ZonedDateTime dateTime = OffsetDateTime.parse(inputStringExample)
.atZoneSameInstant(ZoneId.systemDefault());
System.out.println(dateTime);
Output in my time zone:
2019-11-22T23:08:15+01:00[Europe/Copenhagen]
Parse directly
If you just need a ZonedDateTIme for an API that requires one (for most purposes a poor design), just parse your string into one:
ZonedDateTime dateTime = ZonedDateTime.parse(inputStringExample);
2019-11-23T10:32:15+12:24
Output is indistinguishable from the one we got from OffsetDateTime, but you have got the required type now.
Stay far away from SimpleDateFormat and Date
In the code in your question you tried to use SimpleDateFormat for parsing your string. Since you can use java.time, the modern Java date and time API, stick to it and forget everything about the old date and time classes. The modern API gives you all the functionality you need. In case we had needed a formatter for parsing, the modern DateTimeFormatter would have been the class to use.
What went wrong in your code?
… it does not work due to ParseException: Unparsable data.
Z in your format pattern string is for RFC 822 time zone offset. This is without colon and would have parsed +1224, but not +12:24.
Link
Wikipedia article: ISO 8601
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 );
In my SQLite database, there is no Date datatype, so I have to store timestamps in text format.
Does the format yyyy-MM-dd HH:mm result in the correct ordering such that when you sort it lexicographically (by doing a normal sort ASC or DESC), it also orders by time value inherently?
Your format is the right idea; when sorted alphabetically it is also chronological.
You can take a step further, for a better version of that format, a standard format, to make your work simpler and easier.
ISO 8601
The ISO 8601 standard defines a variety of practical sensible formats for text representing date-time related values.
For a date and time combined the format is:
YYYY-MM-DDTHH:MM:SS.SZ
For example:
2016-04-28T18:22:20.123Z
This format as a string sorts chronologically as you need.
The T in the middle separates the Date portion from the Time portion. The Z on the end is short for Zulu which means UTC.
Generally, best practice is to convert your date-time values to UTC for storage and database. Your JDBC driver likely does that for you but I don't know about SQLite.
java.time
The java.time framework is built into Java 8 and later. Much of that functionality is back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted to Android in the ThreeTenABP project.
These new classes supplant the old java.util.Date/.Calendar and related classes that have proven to be poorly designed and troublesome.
These classes use ISO 8601 formats by default when parsing/generating textual representations of date-time values. Search Stack Overflow for many examples.
An Instant is a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = Instant.now();
Simply call toString to generate a String representation of that value.
String stringForDatabase = instant.toString();
In Java 8 the current moment is captured to only milliseconds resolution due to legacy implementation of the Clock interface, for 3 decimal places for the fraction of a second. For example, 2016-04-29T00:12:57.123Z. In Java 9 and later has a modern implementation of Clock, able to capture the current moment in up to 9 decimal places (nanoseconds) as far as is supported by your computer’s hardware clock.
The default formatter used by Instant:toString prints the fraction of a second with 0, 3, 6, or 9 digits, as many as needed to represent the non-zero portion of the fraction of a second. All of these sort alphabetically & chronologically as requested, so you could store any of these in your database.
2016-04-29T00:12:57Z
2016-04-29T00:12:57.123Z
2016-04-29T00:12:57.123456Z
2016-04-29T00:12:57.123456789Z
These all parse directly back into an Instant instance. So no need to bother with defining your own formatting pattern as in the Question.
Instant instant = Instant.parse( "2016-04-29T00:12:57.123456789Z" );
To see the wall-clock time for a particular place, apply a time zone (ZoneId) to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Extract an Instant for storage back into the database.
Instant instant = zdt.toInstant();
String forDatabase = instant.toString();
Yes. Just that you should not miss any 0's in between.
('04' for April).
It's necessary to represent April as '04', not just '4'.
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.