Java API and client app - how to handle timezone properly - java

I'm developing a Java API that is being used by an android app. I have arrived to a point where I need to correctly handle date and time taking into account timezones.
One of the features of the app is to book some sort of service specifying a date and a time. There is the possibility for the user to cancel the booking and if the cancellation occurs 24h before the booking starts, the client gets all the amount refunded.
Now let's say the server is in London(gmt 0) and the user in Spain(gmt +1) and a booking start on 25-02-2015 at 16:00:00.
When the user cancels a booking the server needs to make the difference between NOW() and the booking start date. So if the user (in spain) makes a cancelation the 24-02-2015 at 17:00:00(spain time, 23hours before booking ; therefore he doesn't get full refund)
when the server checks the difference, because the NOW (it is 16:00:00 in UK) the result will be 24h and therefore will refund full amount wrongly.
My question is here. How do I correctly get the RIGHT amount of hours depending on the user timezone ?
I'm not very happy with sending the client time zone in the cancellation request because the value can easily be tricked by the user.
What is a good practice when storing date and time sever side ? Should I store them in the server time and use an extra field to know the client timezone offset's ?

The Answer by Baranauskas is correct.
Work in UTC
Generally all your business logic, data exchange, data storage, and serialization, should be done in UTC. Apply a time zone only where required/expected, such as presentation to a user.
java.time
Definitely use java.time framework built into Java 8 and later.
By default, the java.time classes use standard ISO 8601 formats for parsing/generating textual representations of date-time values. Otherwise use a DateTimeFormatter object.
The Local… types are generic, not applying to any locality, having no time zone or offset-from-UTC. Generally not used in business apps as they are not actual moments on the timeline. Used here for parsing a date string and a time string separately, combining, and then applying the known time zone for Spain. Doing so produces a ZonedDateTime, an actual moment on the timeline.
LocalDate dateBooking = LocalDate.parse ( "2016-02-25" ); // Parses strings in standard ISO 8601 format.
LocalTime timeBooking = LocalTime.parse ( "16:00:00" );
LocalDateTime localBooking = LocalDateTime.of ( dateBooking , timeBooking );
ZoneId zoneId = ZoneId.of ( "Europe/Madrid" );
ZonedDateTime zonedBooking = localBooking.atZone ( zoneId );
From that we can extract an Instant, a moment on the timeline in UTC.
Instant booking = zonedBooking.toInstant ();
Calculate our 24 hours of required notice for cancellations. Note that 24-hours is not the same as “one day” as days vary in length because of anomalies such as Daylight Saving Time (DST).
Instant twentyFourHoursEarlier = booking.minus ( 24 , ChronoUnit.HOURS );
Get the current moment for the user in process of cancelling. Here we simulate by using the date-time specified in the Question. For shorter code, we adjust by an hour because this parse method handles only UTC (Z) strings. The Question stated 17:00 in Spain time; Europe/Madrid is one hour ahead of UTC so we subtract an hour for 16:00Z.
Instant cancellation = Instant.parse ( "2016-02-24T16:00:00Z" ); // 17:00 in Europe/Madrid is 16:00Z.
// Instant cancellation = Instant.now (); // Use this in real code.
Compare Instant objects by calling the isBefore or isAfter methods.
Boolean cancelledEarlyEnough = cancellation.isBefore ( twentyFourHoursEarlier );
A Duration represents a span of time as a total number of seconds plus a fraction of a second in nanoseconds. We use this here to help with the mathematics, verifying we got the expected result of 23 hours. The toString method uses the standard ISO 8601 durations format of PT23H where P marks the beginning, T separates the years-months-days portion from hours-minutes-seconds portion, and H means “hours”.
Duration cancellationNotice = Duration.between ( cancellation , booking );
Dump to console.
System.out.println ( "zonedBooking: " + zonedBooking + " | booking: " + booking + " | twentyFourHoursEarlier: " + twentyFourHoursEarlier + " | cancellation: " + cancellation + " | cancelledEarlyEnough: " + cancelledEarlyEnough + " | cancellationNotice: " + cancellationNotice );
zonedBooking: 2016-02-25T16:00+01:00[Europe/Madrid] | booking: 2016-02-25T15:00:00Z | twentyFourHoursEarlier: 2016-02-24T15:00:00Z | cancellation: 2016-02-24T16:00:00Z | cancelledEarlyEnough: false | cancellationNotice: PT23H
Present to user by applying their desired/expected time zone. Specify an Instant and a ZoneId to get a ZonedDateTime.
ZoneId zoneId_Presentation = ZoneId.of( "Europe/Madrid" );
ZonedDateTime zdtBooking = ZonedDateTime.ofInstant( booking , zoneId_Presentation );
ZonedDateTime zdtCancellationGrace = ZonedDateTime.ofInstant( twentyFourHoursEarlier , zoneId_Presentation );
ZonedDateTime zdtCancellation = ZonedDateTime.ofInstant( cancellation , zoneId_Presentation );
Let java.time localize for you by specifying a short-medium-long flag and a Locale for the human language in which to (a) translate name of day/month, (b) use cultural norms for ordering of the date-time parts, choosing comma versus period, and such.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL );
formatter = formatter.withLocale( new Locale("es", "ES") );
String output = zdtBooking.format( formatter );
jueves 25 de febrero de 2016 16H00' CET
Ignore Server Time Zone
Your Question mentioned the server’s time zone. You should never depend on the server’s time zone as it is out of your control as a programmer, and it is easily changed with little thought by a sysadmin. In addition, your JVM’s current default time zone may be based on the that of the host operating system. But not necessarily. A flag on the command-line launching of the JVM may set the JVM’s default time zone. Even more dangerous: Any code on any thread of any app within the JVM can set the time zone and immediately affect your app -- during runtime!
If you do not specify a time zone, the JVM’s current default is implicitly applied. Always specify your desired/expected time zone explicitly, as seen in code above.
The above also goes for the current default Locale. Always specify the desired/expected Locale rather than depending implicitly on the JVM’s current default.

The plane is leaving at the same moment in time, no matter where in the world you are. This is also known as a timestamp. For such situations, storing a timestamp would be a proper solution. In java, current timestamp may be retrieved via System.currentTimeMillis(). This value does not depend on the time zone of your server and contains amount of millis since 1970 in UTC.
When user books the flight, you will need to convert user's selected time+timezone into the timestamp. When displaying, timestamps should be converted to user's timezone.
Validation with timestamps is a simple operation then: planeTakeOffTs - NOW > 24*60*60*1000
You may also want to use such library as joda-time (which has been included into inspired Java v8 java.time) to handle the dates and time.

Basil Bourque, to answer your last comment, I did the test below.
As you can see, setting the timezone has effect to LocalDateTime and fortunately.
Where you right is this is not the right solution and should be used if we do not have another solution.
public static void main(String[] args) {
Instant instant = Instant.now();
LocalDateTime local = LocalDateTime.now();
ZonedDateTime zone = ZonedDateTime.now();
System.out.println("====== WITHOUT 'UTC' TIME ZONE ======");
System.out.println("instant : " + instant );
System.out.println("local : " + local);
System.out.println("zone : " + zone);
System.out.println("instant converted : " + instant.atZone(ZoneId.of("Europe/Paris")).toLocalDateTime());
System.out.println("====================================");
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Instant instant2 = Instant.now();
LocalDateTime local2 = LocalDateTime.now();
ZonedDateTime zone2 = ZonedDateTime.now();
System.out.println("====== WITH 'UTC' TIME ZONE ======");
System.out.println("instant2 : " + instant2 );
System.out.println("local2 : " + local2);
System.out.println("zone2 : " + zone2);
System.out.println("instant2 converted : " + instant2.atZone(ZoneId.of("Europe/Paris")).toLocalDateTime());
System.out.println("==================================");
}
And the output :
====== WITHOUT 'UTC' TIME ZONE ======
instant : 2019-02-14T17:14:15.598Z
local : 2019-02-14T18:14:15.682
zone : 2019-02-14T18:14:15.692+01:00[Europe/Paris]
instant converted : 2019-02-14T18:14:15.598
====================================
====== WITH 'UTC' TIME ZONE ======
instant2 : 2019-02-14T17:14:15.702Z
local2 : 2019-02-14T17:14:15.702
zone2 : 2019-02-14T17:14:15.702Z[UTC]
instant2 converted : 2019-02-14T18:14:15.702
==================================
Note that as many developers use Hibernate, it provides this property hibernate.jdbc.time_zone which helps a lot when dealing whith date. Setting this property to UTC has effect to LocalDateTime and ZonedDateTime.

Related

Java Converting 19-digit Unix Timestamp to a Readable Date

I am trying to convert 19 digit Unix timestamp such as 1558439504711000000 (one and a half quintillion) into a readable date/time format. My timestamp ends with 6 zeros which suggests the time is in nano seconds.
I have come across some examples where people have used time zones which I don't need. Another example uses ofEpochSecond like so:
Instant instant = Instant.ofEpochSecond(seconds, nanos);
But I am not sure whether I need to use ofEpochSecond.
The code below gives my most recent approach of achieving this:
String timeStamp = "1558439504711000000";
long unixNanoSeconds = Long.parseLong(timeStamp);
Date date = new java.util.Date(timeStamp*1000L);
// My preferred date format
SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
String formattedDate = sdf.format(date);
System.out.println("The timestamp in your preferred format is: " + formattedDate);
But the output I get is something like this:
// The timestamp in your preferred format is: 11-12-49386951 11:43:20
Which does not show the year format in e.g. 2019 format.
tl;dr
Never use legacy class java.util.Date. Instead, use modern java.time.Instant.
Instant // The modern way to represent a moment in UTC with a resolution of nanoseconds. Supplants the terrible `java.util.Date` class.
.ofEpochSecond( // Parse a count since epoch reference of 1970-01-01T00:00:00Z.
0L , // Passing zero for the count of whole seconds, to let the class determine this number from the 2nd argument.
Long.parse( "1558439504711000000" ) // Count of nanoseconds since the epoch reference of 1970-01-01T00:00:00Z.
) // Returns a `Instant` object.
.atZone( // Adjust from UTC to the wall-clock time used by the people of a specific region (a time zone).
ZoneId.of( "Europe/London" )
) // Returns a `ZonedDateTime` object. Same moment as the `Instant`, same point on the timeline, different wall-clock time.
.format( // Generate text to communicate the value of the moment as seen through this time zone.
DateTimeFormatter.ofPattern( // Define how to format our generated text.
"dd-MM-uuuu HH:mm:ss" , // Specify your desired formatting pattern.
Locale.UK // Pass a `Locale` to be used in localizing, to (a) determine human language used in translating name of day-of-week and such, and (b) determine cultural norms to decide issues of capitalization, abbreviation, etc. Not really needed for this particular formatting pattern, but a good habit to specify `Locale`.
) // Returns a `DateTimeFormatter` object.
) // Returns a `String` object containing our text.
21-05-2019 12:51:44
…or…
Instant
.ofEpochSecond (
TimeUnit.NANOSECONDS.toSeconds(
Long.parse( "1558439504711000000" )
) ,
( 1_558_439_504_711_000_000L % 1_000_000_000L )
)
.toString()
2019-05-21T11:51:44.711Z
Note the hour difference because the time zone is one hour ahead of UTC.
Avoid legacy date-time classes
The java.util.Date class is terrible. Along with its littermates such as Calendar & SimpleDateFormat, they amount to a awful mess. Avoid them. Sun, Oracle, and the JCP community gave up on them when they adopted JSR 310.
Instant
A java.util.Date object represents a moment in UTC, with a resolution of milliseconds. Its replacement is java.time.Instant, also a moment in UTC but with a resolution of nanoseconds. Internally, both track a count since the epoch reference of first moment of 1970 in UTC.
To avoid dealing with gigantic numbers, internally a Instant tracks a number of whole seconds since 1970 plus a fractional second kept as a number of nanoseconds. Two separate numbers. Those are what you need to feed Instant.ofEpochSecond.
Parse your input string as a long using the Long class. By the way, notice that your value is pushing towards to the limit of a 64-bit integer.
long totalNanos = Long.parse( "1558439504711000000" ) ;
Use the TimeUnit enum to do the math of splitting out whole seconds.
long secondsPortion = TimeUnit.NANOSECONDS.toSeconds( totalNanos ) ;
Modulo by a billion, the remainder being the nanoseconds of the fractional second.
long nanosPortion = ( totalNanos % 1_000_000_000L ) ;
Instantiate an Instant.
Instant instant = Instant.ofEpochSecond( secondsPortion , nanosPortion ) ;
My timestamp ends with 6 zeros which suggests the time is in nano seconds.
Actually, nanoseconds count up to a billion, so nine (9) digits not six (6). The fractional second in your count from epoch is 711000000, or 711,000,000 nanos. Your number of whole seconds is 1558439504, or 1,558,439,504 (one and a half billion). As a decimal:
1,558,439,504.711000000 seconds since 1970-01-01T00:00Z
Time Zone
I have come across some examples where people have used time zones which I don't need.
To represent a moment, a specific point on the timeline, you always need a time zone (or offset-from-UTC of hours-minutes-seconds).
To see that same moment through the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime.
Specify a proper time zone name in the format of Continent/Region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 2-4 letter abbreviation such as BST or EST or IST as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, same point on the timeline, different wall-clock time.
2019-05-21T12:51:44.711+01:00[Europe/London]
Notice the adjustment in the time-of-day, going from hour 11 to hour 12. This makes sense as Europe/London zone is an hour ahead of UTC on that date. Same moment, same point on the timeline, different wall-clock time.
Shortcut
As Ole V.V. noted in the comment, you could skip the math discussed above. Feed the entire number of nanoseconds as the second argument to ofEpochSecond. The class internally does the math to separate whole seconds from the fractional second.
Instant instant = Instant.ofEpochSecond( 0L , 1_558_439_504_711_000_000L ) ;
See this code run live at IdeOne.com.
Generate text
Generate text representing the value of that ZonedDateTime in standard ISO 8601 format extended to append the name of the time zone in square brackets.
String output = zdt.toString() ;
2019-05-21T12:51:44.711+01:00[Europe/London]
Or let java.time automatically localize for you.
Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT ).withLocale( locale );
String output = zdt.format( f );
21/05/2019, 12:51
Or specify a custom format.
Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu HH:mm:ss" , locale ) ;
String output = zdt.format( f );
21-05-2019 12:51:44
Tip: Be very careful about providing a date-time without specifying the zone explicitly. This creates ambiguity, where the user may assume a different zone/offset is in play.
I think there is nothing wrong with that, you are dealing with a timestamp that represent a date in the FUTURE (a really far away date in the future).
If you consider this:
String timeStamp = "1558439504";
this should give you: 05/21/2019 # 11:51am (UTC)
Then there is I think an easy way to get the Date. Just create the Instant first based on that timestamp and then do:
Date myDate = Date.from(instant);
Try using this
Date date = new java.util.Date(timeStamp/1000000);
Instead of multiplying by 1000, divide by 1000000

Convert json date to date in java

How to convert json date to java.util.Date
{"date":31,"day":4,"hours":0,"minutes":0,"month":11,"seconds":0,"time":2145805200000,"timezoneOffset":-420,"year":137}
Thanks for help
With a quick Google search, I managed to find this webpage: CLICK
The 'parse' method returning a Date shows you how to use the SimpleDateFormat class in Java which is used to parse a String into a Date.Hope this helped!
No JSON Date
No such thing as a JSON date. JSON has very few data types, none of which are date-time related.
Count Since Epoch
That time item with value 2145805200000 is probably a count since epoch. The two questions are: What epoch? and What granularity of count?
Commonly used are milliseconds since the Unix epoch of first moment of 1970 in UTC.
The java.time framework built into Java 8 and later can translate such number. The Instant class represents a moment on the timeline in UTC.
long sinceEpoch = 2145805200000L;
Instant instant = Instant.ofEpochMilli ( sinceEpoch );
Dump to console.
System.out.println ( "instant: " + instant );
instant: 2037-12-30T17:00:00Z
That value may be close to the other fields in your JSON, but is not quite a match.
Your JSON has an field timezoneOffset with value -420. If we interpret that number as an offset-from-UTC in minutes, that would mean 7 hours. We can ask java.time to adjust the Instant into such an offset.
Normally we would want to use a time zone for this adjustment. A time zone is an offset-from-UTC plus a set of historic rules for handling anomalies such as Daylight Saving Time (DST). But in this case we have only the offset-from-UTC. So rather than use a ZoneId we use the subclass ZoneOffset. The result is a ZonedDateTime.
ZoneOffset zoneOffset = ZoneOffset.ofHours ( 7 );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneOffset );
Dump to console.
System.out.println ( "instant: " + instant + " + zoneOffset: " + zoneOffset + " = zdt: " + zdt );
instant: 2037-12-30T17:00:00Z + zoneOffset: +07:00 = zdt: 2037-12-31T00:00+07:00
That gets us to the beginning of the day on the 31st of December in 2037. Still not an exact match to the other JSON fields.
But if that -420 was meant as being 7 hours behind UTC rather than ahead, we need to change that offset from 7 hours to a negative -7 hours. Then the results would be:
instant: 2037-12-30T17:00:00Z + zoneOffset: -07:00 = zdt: 2037-12-30T10:00-07:00
If you knew more about your JSON data, then perhaps a solution could be found.

Using two LocalDateTime instances to calculate a duration

I am trying to calculate the duration between two instances of LocalDateTime. The special thing here is that each instance of LocalDateTime could be from ANYWHERE in the world:
LocalDateTime start could be from Nevada and LocalDateTime end could be from Tokyo. Each "time" associated with the LocalDateTime is, obviously enough, local to that location.
So if I said...
LocalDateTime start = LocalDateTime.parse("2015-07-14T10:00:00"); and said that start represented Chicago, that would mean 10:00 AM in Chicago time.
Then I could say...
LocalDateTime end = LocalDateTime.parse("2015-07-14T03:00:00"); and end represents Moscow, so it is 3:00AM in Moscow time.
Can I create a robust enough solution that will allow start and end to represent any cities in the world and still correctly calculate the duration between the two?
"LocalDateTime" Does Not Mean A Particular Locality
I think you misunderstand the purpose of LocalDateTime. The "local" means any locality, not a specific locality. As in "Christmas starts at midnight on December 25, 2015" where we mean any locality’s midnight. Christmas starts in Paris several hours earlier than Montréal, for example.
If you know the date-time is meant to represent a date-time in Nevada, the use a Joda-Time DateTime assigned the proper time zone name of America/Boise. In the new java.time package (Tutorial) built into Java 8 and later, use a ZonedDateTime object with assigned time zone.
Similarly, if you know the date-time is local to Tokyo, do not use LocalDateTime. Use a Joda-Time DateTime with an assigned time zone of Asia/Tokyo.
Elapsed
Elapsed time between a pair of LocalDateTime instances makes no sense. For example, the times may be 14:00 and 18:00 on the same date, but that does not mean four hours difference. If you really meant 14:00 in Paris and 18:00 in Chicago, that would be several hours difference, not two.
I am not discussing calculating elapsed time as that has been handled many many times on StackOverflow. I'm trying to clarify some concepts here. Then you can move on to the existing Questions & Answers for calculating elapsed time.
Databases Store UTC
Generally in SQL databases you should be using the data type TIMESTAMP WITH TIME ZONE (a.k.a. TIMESTAMPZ with a Z for "zulu"). This misnomer actually means "with respect for time zone". Incoming data with an offset from UTC or other time zone information is adjusted to UTC. The data's offset or time zone is not preserved.
The SQL data type TIMESTAMP WITHOUT TIME ZONE (a.k.a. TIMESTAMP) means the same as a LocalDateTime in Java: no time zone at all. Not tied to the timeline. Any offset or time zone information with input data is ignored, no adjustment made.
Postgres doc may help explain.
Stick With UTC
When retrieving such a value from the database, the UTC value may be adjusted to a particular time zone by your admin tool (such as pgAdmin in Postgres) or your database driver or by your app.
In your app it is generally best to keep your date-time values in UTC as much as possible. Do nearly all of your storage, business logic, and data exchange in UTC. Only adjust to a particular time zone when expected by the user.
To Convert A LocalDateTime To A Time Zone
If you do have a LocalDateTime object, and you want to assign it a time zone, here is some example code. We also adjust to get the very same moment as seen in Montréal and in UTC. First the example is shown in Joda-Time, then in java.time.
Joda-Time
Example in Joda-Time 2.8.
LocalDateTime ldt = new LocalDateTime( "2015-07-14T10:00:00" ); // Nowhere in particular.
DateTimeZone zoneChicago = DateTimeZone.forID( "America/Chicago" );
DateTime dateTimeChicago = ldt.toDateTime( zoneChicago );
DateTime dateTimeMontreal = dateTimeChicago.withZone( DateTimeZone.forID( "America/Montreal" ) );
DateTime dateTimeUtc = dateTimeChicago.withZone( DateTimeZone.UTC );
Dump to console.
System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + dateTimeChicago );
System.out.println( "Montréal: " + dateTimeMontreal );
System.out.println( "UTC: " + dateTimeUtc);
When run.
LocalDateTime (nowhere): 2015-07-14T10:00:00.000
Chicago: 2015-07-14T10:00:00.000-05:00
Montréal: 2015-07-14T11:00:00.000-04:00
UTC: 2015-07-14T15:00:00.000Z
java.time
Example in java.time of Java 8 Update 51.
LocalDateTime ldt = LocalDateTime.parse( "2015-07-14T10:00:00" ); // Nowhere in particular.
ZoneId zoneChicago = ZoneId.of( "America/Chicago" );
ZonedDateTime zdtChicago = ZonedDateTime.of( ldt, zoneChicago );
ZonedDateTime zdtMontreal = zdtChicago.withZoneSameInstant( ZoneId.of( "America/Montreal" ) );
ZonedDateTime zdtUtc = zdtChicago.withZoneSameInstant( ZoneOffset.UTC ); // ZoneOffset is a subclass of ZoneId.
Dump to console.
System.out.println( "LocalDateTime (nowhere): " + ldt );
System.out.println( "Chicago: " + zdtChicago );
System.out.println( "Montréal: " + zdtMontreal );
System.out.println( "UTC: " + zdtUtc);
When run.
LocalDateTime (nowhere): 2015-07-14T10:00
Chicago: 2015-07-14T10:00-05:00[America/Chicago]
Montréal: 2015-07-14T11:00-04:00[America/Montreal]
UTC: 2015-07-14T15:00Z

Date gives one day less than actual date while converting from date to calendar

I am passing date from front end which is IST(date of indian timezone). And in java code i am converting date to calendar using the following code(This is happening in the server which is there in US PST timezone).
Calendar cal = Calendar.getInstance();
int offset = date.getTimezoneOffset();
logger.info("Calendar Instance - " + cal);
cal.setTime(date);
logger.info("Calendar Instance after setting date - " + cal);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
logger.info("Calendar Instance after setting zeros - " + cal);
return cal;
so when i see the last log the day of the month will be one day less than what i passed.eg. if i pass 22/06/2015 IST it shifts to 21/06/2015. so after processing finally it displays 21/06/2015 in the list of data which is in another UI page.
This happens because JVM on server side and JVM on client side use different time zones by default Java TimeZone:
Typically, you get a TimeZone using getDefault which creates a
TimeZone based on the time zone where the program is running. For
example, for a program running in Japan, getDefault creates a TimeZone
object based on Japanese Standard Time.
As we can see, Pacific Time Zone on server has UTC−8:00 and Indian Standard Time on client has UTC+05:30. They differ by 13.30 and Indian date X converts to US as X-13.30 what may yield a day before on server side for certain X.
Several workarounds are possible depending on how you can influence/modify your server and client application. For example, you may work with dates in UTC+00:00 time zone on both server and client sides. If you need to show a date to the user you may convert it to Indian time zone when needed.
// Set default GMT+0:00 time zone
TimeZone timeZone;
timeZone = TimeZone.getTimeZone("GMT+0:00");
TimeZone.setDefault(timeZone);
Instead of simply using Calendar cal = Calendar.getInstance(); you may create "clear" calendar which you will user later on to set day, month and year
public static Calendar createClearedCalendar() {
Calendar cal = Calendar.getInstance();
cal.setTimeZone(timeZone);
cal.set(1970, 0, 1, 0, 0, 0);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.clear(Calendar.MILLISECOND);
return cal;
}
By the way, if you manipulate date-time in Java you may consider Joda Time which has more extended options and optimized performance.
The Answer by Antonio is correct and should be accepted (click the big empty check mark).
This Answer adds some thoughts and example code.
Avoid 3-Letter Time Zone Codes
Avoid using, or even thinking about, those 3 or 4 letter codes such as IST or PST. They are not standardized, they are not unique, and they further confuse issues around Daylight Saving Time (DST). For example, IST means "India Standard Time", "Irish Standard Time", and more.
Use proper time zone names. Most of these are in a "continent" + "/" + "city/region" pattern. The city/region name is not meant specifically for that town, but rather as an easily identifiable name for as wide an area as possible that shares the same set of past, present, and future rules for time zone rules and anomalies (including DST).
Use UTC
Generally you should be using UTC time zone for all your business logic, data storage, and data exchange. Adjust to a particular time zone only for presentation when expected by the user.
Use A Decent Date-Time Framework
The old java.util.Date/.Calendar classes were a bold attempt at handling date-time work, but ultimately they failed. They are notoriously troublesome, flawed in both design and implementation. Avoid them.
The 3rd-party Joda-Time library is one solution. It works in many versions of Java and also in Android. Joda-Time inspired the other solution, the java.time package found in Java 8 and later (Tutorial).
Solution
The Question seems to have a goal of taking a java.util.Date object, assign desired time zone, and produce a java.util.Calendar object.
Fortunately the java.time framework has conversion methods. See this Tutorial page.
Example code follows, using java.time from Java 8 Update 45.
You may want imports such as:
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
Let's simulate getting a java.util.Date passed in. We'll instantiate a Date based on "now".
Date inputDate = new Date( ); // Simulate getting a java.util.Date object.
Then we define the desired time zones, using proper time zone names. Let’s throw in Montréal just for fun as well as the pacific United States and India time zones mentioned in the Question.
ZoneId zoneLosAngeles = ZoneId.of( "America/Los_Angeles" );
ZoneId zoneMontréal = ZoneId.of( "America/Montreal" );
ZoneId zoneKolkata = ZoneId.of( "Asia/Kolkata" );
Then we convert that to an Instant, a point on the timeline without regard to time zone.
Instant instant = inputDate.toInstant( );
Then we assign various time zones to create ZonedDateTime instances. See how we can instantiate a ZonedDateTime in either of two ways: [a] from an Instant, or [b] from another ZonedDateTime via the withZoneSameInstant method. Both ways are shown below.
Note that java.time (and Joda-Time) uses immutable objects, a design pattern where we create new instances based on the old instance rather than alter ("mutate") the old instance. Thread-safety is one of the major benefits.
ZonedDateTime zdtLosAngeles = ZonedDateTime.ofInstant( instant, zoneLosAngeles );
ZonedDateTime zdtMontréal = ZonedDateTime.ofInstant( instant, zoneMontréal );
ZonedDateTime zdtKolkata = ZonedDateTime.ofInstant( instant, zoneKolkata );
ZonedDateTime zdtUtc = zdtKolkata.withZoneSameInstant( ZoneOffset.UTC );
Lastly, we convert one of those to a GregorianCalendar object which is a subclass of java.util.Calendar.
GregorianCalendar calendarKolkata = GregorianCalendar.from( zdtKolkata );
Dump to console.
System.out.println( "inputDate: " + inputDate );
System.out.println( "zdtLosAngeles: " + zdtLosAngeles );
System.out.println( "zdtMontréal: " + zdtMontréal );
System.out.println( "zdtKolkata: " + zdtKolkata );
System.out.println( "zdtUtc: " + zdtUtc );
System.out.println( "calendarKolkata: " + calendarKolkata );
When run.
inputDate: Wed Jun 24 15:12:12 PDT 2015
zdtLosAngeles: 2015-06-24T15:12:12.153-07:00[America/Los_Angeles]
zdtMontréal: 2015-06-24T18:12:12.153-04:00[America/Montreal]
zdtKolkata: 2015-06-25T03:42:12.153+05:30[Asia/Kolkata]
zdtUtc: 2015-06-24T22:12:12.153Z
calendarKolkata: java.util.GregorianCalendar[time=1435183932153,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Kolkata",offset=19800000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2015,MONTH=5,WEEK_OF_YEAR=26,WEEK_OF_MONTH=4,DAY_OF_MONTH=25,DAY_OF_YEAR=176,DAY_OF_WEEK=5,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=3,HOUR_OF_DAY=3,MINUTE=42,SECOND=12,MILLISECOND=153,ZONE_OFFSET=19800000,DST_OFFSET=0]

How to transfer this string to dateformat

I tried to use SimpleDateFormat to do the work,
but I don't know how to handle the T in the string "2008-08-01T15:47:00.557", can anyone help me with this?
You need to use the format "yyyy-MM-dd'T'hh:mm:ss.SSS".
In an additional note, if you are trying to handle xml dates check out this question: Convert Java Date into XML Date Format (and vice versa)
I'm not very very sure. But if I remember good, you have to surround the T by single quotes in your format.
String yourFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS";
since your example was with 24H format and not AM/PM one
you should use HH (capital) instead of hh
like this
String EXT_JS_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
Almost this exact example is given in the API, check it out :-)
http://download.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
ISO 8601
Your string’s format happens to comply with the ISO 8601 standard.
java.time
Java 8 and later includes the java.time framework to supplant the old date-time classes used in the Question and in other Answers.
The new classes use the ISO 8601 standard by default when parsing/generating strings. So no need to specify a coded format pattern.
Time Zone
Your input string lacks any time zone or offset-from-UTC. So you must specify the time zone for which this string has meaning. If you do not specify, the parsing automatically applies your JVM’s current default time zone. Not good as that default may not be the zone intended for your string. Also, the JVM’s default can change at any moment, even during runtime.
If UTC
If your string was meant for UTC as the time zone, simply append a Z (short for “Zulu” which means UTC). Then parse as an Instant, a moment on the timeline in UTC.
String input = "2008-08-01T15:47:00.557";
Instant instant = Instant.parse ( input + "Z" );
Dump to console.
System.out.println ( "instant: " + instant );
instant: 2008-08-01T15:47:00.557Z
If Time Zone
If your string was intended for some other time zone, we need to specify. Use a proper time zone name (never the 3-4 letter codes seen in the press). Here we arbitrarily choose the Montréal time zone.
For the formatting pattern, we use one of the predefined formats for ISO 8601: DateTimeFormatter.ISO_LOCAL_DATE_TIME (the 'LOCAL' means no time zone or offset embedded within the input string).
String input = "2008-08-01T15:47:00.557";
ZoneId zoneId = ZoneId.of ( "America/Montreal" );
DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
formatter = formatter.withZone ( zoneId );
ZonedDateTime zdt = ZonedDateTime.parse ( input , formatter );
Dump to console. We also extract an Instant from the ZonedDateTime so you can see the same moment in UTC. Usually best to work in UTC in your business logic; only apply a time zone for output to the user.
System.out.println ( "input: " + input + " | zdt: " + zdt + " | instant of zdt: " + zdt.toInstant () );
input: 2008-08-01T15:47:00.557 | zdt: 2008-08-01T15:47:00.557-04:00[America/Montreal] | instant of zdt: 2008-08-01T19:47:00.557Z

Categories

Resources