Cannot parse date string coming from MongoDB in Android/Java - java

I am trying to parse date (2015-06-25T00:00:00.000Z) coming from MongoDB.
try {
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH);
Date date = format.parse("2015-06-25T00:00:00.000Z");
return new Date().after(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
Where am I wrong?

Your pattern accepts only second digits but your string has fractional seconds as well
yyyy-MM-dd'T'HH:mm:ss 'Z'
2015-06-25 T 00:00:00.000 Z
You need to use
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"

Do not ignore the Z
Your formatting pattern has single quote marks around the Z. That means to interpret the "Z" as mere text, to expect it but then ignore it. Very bad, as that Z has meaning: short for Zulu, and means UTC time zone.
By ignoring the Z, parsing will implicitly apply your JVM’s current default time zone. Your input meant the first moment of the day in UTC (stroke of midnight), but in the following code you can see the value is mis-interpreted as midnight in my default zone of America/Los_Angeles -- an error of 7 hours (my zone’s current offset from UTC).
try {
java.text.DateFormat format = new SimpleDateFormat ( "yyyy-MM-dd'T'HH:mm:ss'Z'" , Locale.ENGLISH );
java.util.Date date = format.parse ( "2015-06-25T00:00:00Z" );
System.out.println ( "date: " + date );
} catch ( ParseException e ) {
e.printStackTrace ();
}
In this output, PDT means the Daylight Saving Time (DST) version of Pacific time (more accurately named as America/Los_Angeles). Note the time is 00:00:00 but that is wrong -- should have been seven hours earlier on the previous day as my zone is behind UTC.
date: Thu Jun 25 00:00:00 PDT 2015
java.time
This work would be much easier with the java.time framework built into Java 8 and later. For Java 6 & 7, use the back-port, ThreeTen-Backport. For Android, the adaptation of that back-port, ThreeTenABP.
The java.time classes use ISO 8601 format by default when parsing/generating textual representations of date-time values. Your input happens to comply fully with ISO 8601. So no need to specify a formatting pattern.
An Instant is a moment on the timeline in UTC.
String input = "2015-06-25T00:00:00Z";
Instant instant = Instant.parse ( input );
Apply a proper time zone name if desired.
ZoneId zoneId = ZoneId.of ( "America/Los_Angeles" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Dump to console.
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt );
input: 2015-06-25T00:00:00Z | instant: 2015-06-25T00:00:00Z | zdt: 2015-06-24T17:00-07:00[America/Los_Angeles]
Note how in this output, the same input is now correctly adjusted to my America/Los_Angeles time zone by moving back seven hours into the previous date.
Convert to/from java.time
Avoid using java.util.Date. The old date-time classes really are that bad. But if required, you can convert between java.time types and the old types. In this case, by using the count-from-epoch in milliseconds. Be aware that you may lose a bit of data, going from the nanoseconds resolution to milliseconds in java.util.Date (not in this case, but perhaps in other cases).
java.util.Date utilDate = new java.util.Date( instant.toEpochMilli() );

Related

Why does SimpleDateFormat.format(Date) ignore the configured TimeZone?

Given that my default TimeZone is Europe/Paris:
System.out.println("Current Timezone: " + TimeZone.getDefault());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
String dateStrIn = "2016-08-01T00:00:00Z";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
The output is:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 02:00:00 CEST 2016
Output date String: 2016-08-01T02:00:00+02
Now, I repeat the execution but setting a different default TimeZone (UTC):
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
...
And this is the output:
Current Timezone: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 00:00:00 UTC 2016
Output date String: 2016-08-01T02:00:00+02
Date.toString() has correctly taken into account the TimeZone change. However, the String gotten from dateFormat.format(date); is still showing +02 instead of Z or +00, why?
Using standard Java API, is there any way to force the formatting according to a selected TimeZone?
UPDATE:
Jesper's solution works in almost all cases, but I have come across this one (using Canary Island's summer time zone WEST) in which it doesn't:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WEST";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
Output:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WEST
Date.toString() Mon Aug 01 09:00:00 CEST 2016
Output date String: 2016-08-01T08:00:00 WEST
You can see that Output date String is still expressed in WEST.
If, for instance, I change the initial dateStrIn to: String dateStrIn = "2016-08-01T08:00:00 GMT";, then the Output date String is expressed in CEST as espected:
Output date String: 2016-08-01T10:00:00 CEST
Could it be a bug?
UPDATE 2:
Another example
Default TimeZone for both Date and SimpleDateFormat: "Europe/Paris"
Input String: "2016-08-01T08:00:00 WET"
Output:
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T09:00:00 WEST
Notice that dateFormat.format(date); has produced a WEST date string. From WET -> WEST when it should be WET -> CEST.
Instead of setting the default time zone, set the time zone on your SimpleDateFormat object:
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
This will make the SimpleDateFormat object format your Date object in the UTC time zone.
If you're using Java 8, consider using the new date and time API in the package java.time instead of the old java.util.Date and java.text.SimpleDateFormat.
You torture yourself by using the troublesome old legacy date-time classes now outmoded by the java.time framework.
tl;dr
ZonedDateTime.ofInstant( Instant.parse( "2016-08-01T00:00:00Z" ) , ZoneId.of( "Europe/Paris" ) )
java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat. The Joda-Time team also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Instant
The Z on the end of your input string 2016-08-01T00:00:00Z is short for Zulu and means UTC. Represented in java.time by the Instant class with a resolution up to nanoseconds.
Instant instant = Instant.parse ( "2016-08-01T00:00:00Z" );
ZonedDateTime
Specify a time zone via ZoneId to get a ZonedDateTime.
In the summer, Paris time is two hours ahead of UTC. So the same moment as seen on a clock on the wall in Paris is 2 AM rather than midnight, on same date of August 1st.
ZoneId zoneId_Paris = ZoneId.of ( "Europe/Paris" );
ZonedDateTime zdt_Paris = ZonedDateTime.ofInstant ( instant , zoneId_Paris );
You can adjust into another time zone such as Atlantic/Canary. For this date in the summer, the time is one hour ahead of UTC. Result is 1 AM rather than midnight.
ZoneId zoneId_Canary = ZoneId.of ( "Atlantic/Canary" );
ZonedDateTime zdt_Canary = zdt_Paris.withZoneSameInstant ( zoneId_Canary );
Dump to console.
System.out.println ( "instant: " + instant + " | zdt_Paris: " + zdt_Paris + " | zdt_Canary: " + zdt_Canary );
instant: 2016-08-01T00:00:00Z | zdt_Paris: 2016-08-01T02:00+02:00[Europe/Paris] | zdt_Canary: 2016-08-01T01:00+01:00[Atlantic/Canary]
All three of these objects (UTC, Paris, Canary) represent the very same simultaneous moment in history, the same single point on the timeline. Each is viewed through the lens of a different wall-clock time.
Real Time Zones
Avoid 3-4 letter zone abbreviations such as WET and WEST. These are not real time zones, not standardized, and not even unique(!).
Europe/Paris and Atlantic/Canary are proper time zone names, in the format of continent/region.
The answer to this mystery was on the Javadoc:
DateFormat.parse(String source, ParsePosition pos)
This parsing operation uses the calendar to produce a Date. As a
result, the calendar's date-time fields and the TimeZone value may
have been overwritten, depending on subclass implementations. Any
TimeZone value that has previously been set by a call to setTimeZone
may need to be restored for further operations.
Setting the TimeZone after parsing works it out:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WET";
Date date = dateFormat.parse(dateStrIn);
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
Right output:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WET
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T10:00:00 CEST
No, its not a bug.
You have to first understand how Date class works. It is nothing but a wrapper over the number of milliseconds since the epoch, expressed in long. Hence, whichever timezone it may be, the underlying value of the date object remains the same. You can never really change the timezone of a Date class. You can only represent a String format of a date instance using a SimpleDateFormat class. This representation might have different time zones, based on which you are using while creating the SimpleDateFormatobject.
Again, you need to check the toString method of the Date class. It always prints the date with the default time zone.
Edit
You should look at the SimpleDateFormat.parse() definition as well. The JDK says that,
The TimeZone value may be overwritten, depending on the given pattern and the time zone value in text. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.

Converting UTC timestamp to local date

So I tried now for about hours to convert a Timestamp to a local date (CEST).
Date date = new Date(stamp*1000);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("CEST"));
String myDate = simpleDateFormat.format(date);
It's not working whatever I tried and looked up in Internet I always get back the UTC time......
for better understanding: stamp is a variable timestamp with type long which I will receive from a service
tl;dr
String output = ZonedDateTime.ofInstant ( Instant.ofEpochSecond ( 1_468_015_200L ) , ZoneId.of ( "Europe/Paris" ) ).toString();
Details
A few issues:
You are not using proper time zone names.
Proper names are in continent/region format.
The 3-4 letter abbreviations so commonly seen in the media such as CEST are not true time zones. Avoid them. They are neither standardized nor unique(!).
You are using old outmoded date-time classes that are poorly designed and confusing. They have been supplanted by the java.time framework.
If by CEST you meant 2 hours ahead of UTC in the summer, then let's take Europe/Paris as an example time zone. Your Question lacks example data, so I'll make up this example.
Apparently your input is a count of whole seconds from the epoch of first moment of 1970 in UTC. That value can be used directly, no need to multiply.
The ZoneId class represents the time zone. An Instant is a point on the timeline in UTC with a resolution up to nanoseconds. A ZonedDateTime is the Instant adjusted into the ZoneId.
ZoneId zoneId = ZoneId.of ( "Europe/Paris" );
long input = 1_468_015_200L; // Whole seconds since start of 1970.
Instant instant = Instant.ofEpochSecond ( input );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant , zoneId );
Dump to console.
System.out.println ( "input: " + input + " | instant: " + instant + " | zdt: " + zdt );
input: 1468015200 | instant: 2016-07-08T22:00:00Z | zdt: 2016-07-09T00:00+02:00[Europe/Paris]
Your TimeZone id is likely to be incorrect (well, not recognized by Java). It seems that instead of throwing an exception the TimeZone is evaluated to UTC in that case.
Try this instead:
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("ECT"));
Here is a page giving some information about Java's TimeZone and a list of timezone ids.

SimpleDateFormat is behaving strange in Android

Following is the code that I have used to get the 00 hour of the current day (in long format).
I am running the below code in android.
The method returns the value properly most of the time. But once in a while it returns the value of System.currentTimeMillis().
import java.text.SimpleDateFormat;
import java.util.Date;
public static final SimpleDateFormat SD_FORMAT_DAY_MONTH_YEAR = new SimpleDateFormat("dd/MM/yyyy");
public static long getLongForCurrent00hr() {
Date date = new Date();
String time = SD_FORMAT_DAY_MONTH_YEAR.format(date);
long value;
try {
Date date2 = SD_FORMAT_DAY_MONTH_YEAR .parse(time);
value = date2.getTime();
} catch (ParseException e) {
value = 0;
}
return value;
}
Why is it returning the System.currentTimeMillis()?
How can I solve the issue?
I am more interested in knowing WHY..
As I was ruuning this code today, I checked it by putting Logs:
Most of the time it returns: 1462386600000
And few times System.currentTimeMillis() like 1462430867302.
Your example code works
I see no problem with your code, as you frame it (read below for criticism).
Nearly your exact code is shown here. Two changes:
I made your format constant a local variable. (simply to make this demo easier, one block of code that can be copy-pasted)
I added a couple calls to get an Instant, the current moment in UTC. Similar to java.util.Date, but Instant::toString creates a string showing UTC rather than confusingly applying the JVM’s current time zone. So you can more clearly see that you are indeed getting the first moment of the day of your JVM’s current default time zone. In my case when running this code, my JVM’s current default time zone is America/Los_Angeles, currently in Daylight Saving Time (DST) for an offset-from-UTC for -07:00 (seven hours behind UTC).
Example code.
Date date1 = new Date ();
SimpleDateFormat SD_FORMAT_DAY_MONTH_YEAR = new SimpleDateFormat ( "dd/MM/yyyy" );
String time = SD_FORMAT_DAY_MONTH_YEAR.format ( date1 );
Date date2 = null;
long value;
try {
date2 = SD_FORMAT_DAY_MONTH_YEAR.parse ( time );
value = date2.getTime ();
} catch ( ParseException e ) {
value = 0;
}
System.out.println ( "date1: " + date1 + " date2: " + date2 + " value: " + value + " | instant 1: " + date1.toInstant () + " | instant 2: " + date2.toInstant () );
When run.
date1: Thu May 05 16:55:40 PDT 2016 date2: Thu May 05 00:00:00 PDT 2016 value: 1462431600000 | instant 1: 2016-05-05T23:55:40.907Z | instant 2: 2016-05-05T07:00:00Z
Working too hard
Your Question is confusing, but it seems that you are trying to capture the first moment of the day. You are going about it the wrong way, and are working too hard.
Time Zone
Your code appears to be working with the java.util.Date class. That class represents a moment on the timeline in UTC.
But you are not getting the first moment of the day in UTC. When you parse that date-only string to generate a new java.util.Date (a date plus time-of-day value, despite the misleading name), your JVM’s current default time zone is applied implicitly. Very confusing to have time zones invisibly injected into the process.
Instead you should consciously consider time zones, and always make the time zone explicit is your coding (as seen below).
java.time
The old java.util.Date/.Calendar classes have proven to be poorly designed, confusing, and troublesome. They are now legacy, supplanted by the java.time framework built into Java 8 and later. Much of the java.time functionality is back-ported to Java 6 & 7 and further adapted for Android.
For a date-only value without time-of-day and without time zone, use LocalDate class. While not storing a time zone, determining a date such as “today” requires a time zone. If omitted, your JVM’s current default time zone is applied (beware, that default can change at any moment during runtime).
ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
You seem to want the first moment of the day. Do not assume the time of that moment is 00:00:00.0. While often true, in some time zones an anomaly such as Daylight Saving Time may shift to another time. Let java.time determine the correct time. Calling [atStartOfDay][2] generates a ZonedDateTime for the first moment appropriate to the specified time zone.
ZonedDateTime zdt = today.atStartOfDay( zoneId );
I strongly recommend against using handling date-time values as a count-from-epoch. That is like using an array of ints of Unicode code points rather than using the String-related classes for handling text. But if you insist, you can convert. But beware data loss as the java.time classes have a finer resolution of nanoseconds whereas you are asking for milliseconds (one of many reasons to avoid handling date-time as a count-from-epoch). First extract an Instant, a moment on the timeline in UTC with a resolution of nanoseconds.
Instant instant = zdt.toInstant();
long millisecondsFromEpoch = instant.toEpochMilli(); // WARNING: Possible data loss (going from nanoseconds to milliseconds).
UTC
If you did want the first moment of the day in UTC, that too is easy.
You could specify UTC as the time zone, using the constant ZoneOffset.UTC. (That constant happens to be in ZoneOffset, a subclass of ZoneId.)
ZonedDateTime zdt = today.atStartOfDay( ZoneOffset.UTC );
But that may not be the most appropriate route. A time zone is an offset-from-UTC plus a set of rules for anomalies such as Daylight Saving Time (DST). UTC has no such anomalies by definition. So more appropriate would be the OffsetDateTime rather than ZonedDateTime.
OffsetTime ot = OffsetTime.of( 0 , 0 , 0 , 0 , ZoneOffset.UTC );
OffsetDateTime odt = today.atTime( ot );

Gson deserialize Java.util.Date with timezone

I deserialize a json string with date:
"created_at": "2015-12-24T17:41:54+01:00",
I set date format for gsonBuilder:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").create();
The deserialization works without crash; unfortunately when I print the result it's not correct:
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd'T'HH:mm:ssZ");
String str = ft.format(response.createdAt);
The result is:
2015-12-24T11:41:54-0500
instead of:
2015-12-24T17:41:54+01:00
You haven't set the timezone only added a Z to the end of the date/time, so it will look like a GMT date/time but this doesn't change the value.
Set the timezone to GMT and it will be correct.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
java.time
The accepted Answer is correct. But outdated. The java.text.SimpleDateFormat and java.util.Date classes have been outmoded by the java.time framework added to Java 8 and later. Much easier to use and more sensible.
ISO 8601
The format of your input string complies with the ISO 8601 standard. The java.time classes use ISO 8601 as their default formats when parsing/generating string representations of date-time values.
String input = "2015-12-24T17:41:54+01:00";
Offset-From-UTC
That input string includes an offset-from-UTC of +01:00, meaning an hour ahead of UTC.
ZonedDateTime zdt = ZonedDateTime.parse ( input );
ZoneId z = zdt.getZone ();
Offset versus Time Zone
But that offset is not a time zone. A time zone is an offset plus rules for handling anomaly adjustments such as Daylight Saving Time (DST). So you may well want to assign a specific time zone you have in mind as being intended by that string. Perhaps you intended Amsterdam time.
We can apply a time zone to get another ZonedDateTime object. This pattern of creating a new object based on an old object’s values rather than changing the old values directly is known as immutable objects.
A ZoneId is a full time zone in java.time. Its subclass ZoneOffset is for simple offset-from-UTC values without the adjustment rules.
ZoneId zoneIdAmsterdam = ZoneId.of ( "Europe/Amsterdam" );
ZonedDateTime zdtAmsterdam = zdt.withZoneSameInstant ( zoneIdAmsterdam );
toString
When you call toString such as in a System.out.println, java.time classes generate a String representation of the date-time value using ISO 8601 format.
Note that java.time extends ISO 8601 by appending the name of an assigned time zone in square brackets in addition to the offset-from-UTC number. For example, [Europe/Amsterdam].
System.out.println ( "zdt: " + zdt + " at zoneId z: " + z + " adjusted to zoneIdAmsterdam: " + zoneIdAmsterdam + " is zdtAmsterdam: " + zdtAmsterdam );
zdt: 2015-12-24T17:41:54+01:00 at zoneId z: +01:00 adjusted to zoneIdAmsterdam: Europe/Amsterdam is zdtAmsterdam: 2015-12-24T17:41:54+01:00[Europe/Amsterdam]
Instant
Generally in our business logic and data storage we work strictly in UTC, applying a time zone only for presentation to the user. For this purpose, pass and store an instance of the Instant class. This class represents a moment on the timeline in UTC.
Instant instant = zdt.toInstant();

What is the easiest way to get a formatted string from a calendar object which respects timezone?

When I search online about "how to convert a Calendar to a String", all the results I find suggest to first convert to a Date and then convert the Date to a String.
The problem is that a Date is only a representation of the number of milliseconds since the epoch - it does not respect timezone. Calendar is more advanced in this way.
Of course, I could call the individual Calendar.get methods to create my own formatted string, but surely there must be an easier way?
To illustrate, I wrote this code:
long currentTime = Calendar.getInstance().getTimeInMillis();
Calendar calendar = new GregorianCalendar();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
calendar.setTimeInMillis(currentTime);
System.out.println(calendar.getTime().toString());
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
While running this code from a machine based in London (UTC+0) at 8:02pm, I got the following results:
Wed Nov 18 20:02:26 UTC 2015
2015-11-18 20:02:26
21
The last line shows the real hour according to the calendar's timezone (Madrid which is UTC+1). It is 9:02pm in Madrid, but obviously both the native Date.toString as well as the DateFormat.format methods ignore the timezone because the timezone information is erased when calling Calendar.getTime (similarly Calendar.getTimeInMillis).
Given this, what is the best way to get a formatted string from a Calendar which respects timezone?
Set the timezone on the SimpleDateFormat object and then use z ..
sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
System.out.println(sdf.format(calendar.getTime());
See here for details on how to handle timezones in Java.
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
Calendar cal = Calendar.getInstance();
System.out.println(simpleDateFormat.format(cal.getTime()));
java.time
While the other Answers appear to be correct, a better approach is to avoid using java.util.Date/.Calendar entirely.
Those old date-time classes have been superseded by the java.time framework built into Java 8 and later. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
Instant
An Instant represents a moment on the timeline in UTC.
Instant instant = Instant.now ( ); // Current moment in UTC.
For a given Calendar object, convert to an Instant using the method toInstant added in Java 8.
Instant instant = myCalendar.toInstant();
ZonedDateTime
You can assign a time zone (ZoneId) to an Instant to get a ZonedDateTime.
ZoneId zoneId = ZoneId.of ( "Europe/Madrid" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant, zoneId );
String Representation of Date-Time Value
Dump to console.
System.out.println ( "instant: " + instant + " adjusted into zone: " + zoneId + " is zdt: " + zdt );
The java.time classes use ISO 8601 standard formatting by default when parsing/generating String representations of date-time values. By default the ISO 8601 style is extended by appending the name of the time zone in addition to the usual offset-from-UTC.
instant: 2015-11-18T22:23:46.764Z adjusted into zone: Europe/Madrid is zdt: 2015-11-18T23:23:46.764+01:00[Europe/Madrid]
If you want the ISO 8601 style but without the T, either call .replace( "T" , "" ) on the resulting String object or define your own formatter.
The java.time.format package can do the work of determining a localized format appropriate to a particular Locale.
Locale locale = Locale.forLanguageTag ( "es-ES" );
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL );
String output = zdt.format ( formatter.withLocale ( locale ) );
miércoles 18 de noviembre de 2015 23H38' CET
You can use String.format() to avoid timezone problems
http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html
This example gives a result in the format: "yyyy-MM-dd HH:mm:ss"
Calendar c = Calendar.getInstance();
String s = String.format("%1$tY-%1$tm-%1$td:%1$tM:%1$tS", c);
System.out.println(s);
Output:
2015-11-20:44:55

Categories