So I've been working on a simple little method which would return a String as Calendar object.
Here's what I have:
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.ENGLISH);
try {
cal.setTime(sdf.parse(this.getCreated_at()));
}
catch (ParseException e) {
e.printStackTrace();
}
return cal;
Now assuming that the String obtained from this.getCreated_at()) is always in the desired format, this should, theoretically, work perfectly.
There's only one little hiccup.
Assume that this.getCreated_at()) returns Mon Feb 10 18:52:54 +0000 2014. Shouldn't be a problem right? The format is correct, and everything.
However when I have my main method do as follows:
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy");
System.out.println(sdf.format(... ... ....stringToCalendar().getTime()));
It gives me the following output:
ma feb 10 19:52:54 +0100 2014
My problem, as you may have noticed, is that firstly, the time is wrong. It's added an hour.
Secondly, it's translated the text fields Mon and Feb into ma and feb which are translations to Norwegian, the place from where I'm coding now.
I need the time returned to me as a Calendar object, in the exact manner which it was obtained from the this.getCreated_at())-string.
What am I doing wrong?
Can anyone help me out?
I'm from Norway, by the way. That's +01:00 Standard Offset.
My problem, as you may have noticed, is that firstly, the time is wrong. It's added an hour.
No it hasn't. Not really. It's displaying the exact same time, but in your local time zone. Unfortunately you can't easily determine the original time zone, because DateFormat.parse returns a Date, which doesn't contain any time zone information. (In this case you don't really know the full time zone anyway - you just know the UTC offset, which isn't quite the same thing.)
EDIT: As noted in Joni's answer, it appears that DateFormat.parse does retain the original information in the format's calendar; you wouldn't want the time zone, but you could use the offset stored in the Calendar object. Personally this seems to me to be an implementation detail which I wouldn't want to rely on (especially as you end up with an offset which isn't necessarily supported by the time zone in the same calendar object!) but you could mangle it if you really wanted.
Secondly, it's translated the text fields Mon and Feb into ma and feb which are translations to Norwegian, the place from where I'm coding now.
That's because your SimpleDateFormat is using your default locale.
You can fix this part very easily:
SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy",
Locale.ENGLISH);
If you know what time zone you want to use, you can specify that on the SimpleDateFormat as well. Otherwise, you're going to have a slightly harder time.
I would recommend that instead, you start using Joda Time. That way you can parse the value as a DateTime, which "knows" which time zone it's in - so you can reformat it however you want, preserving the relevant time zone information.
Sample Joda Time code:
import java.util.*;
import org.joda.time.*;
import org.joda.time.format.*;
class Test {
public static void main(String[] args) {
DateTimeFormatter format = DateTimeFormat
.forPattern("EEE MMM dd HH:mm:ss Z yyyy")
.withLocale(Locale.ENGLISH);
String input = "Mon Feb 10 18:52:54 +0000 2014";
DateTime value = format.parseDateTime(input);
System.out.println(format.print(value));
}
}
You can get the Calendar instance from the date format:
sdf.parse(this.getCreated_at());
Calendar parsed = sdf.getCalendar();
This instance has the fields set to what was parsed: UTC timezone, time 18:52:54, etc...
Unfortunately there appears to be no way to format the values held by Calendar using SimpleDateFormat.
Related
I am using the below code for epoch to time conversion by using java.util.Date class in Java.
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
System.out.println("Date obj :" + date);
Below are the outputs while running the same code on two different timezone server :
On EDT server-
Date obj :Fri Oct 09 11:45:00 EDT 2020
On IST server -
Date obj :Fri Oct 09 21:15:00 IST 2020
Why does this happen? I am only passing milliseconds. This data is supposed to be treated as 21:15 on all servers. Why does Date class change the data?
Please share a sample piece of code for getting the same time data regardless of the timezone of the server.
A Date object represents a specific instant in time, represented by a given number of milliseconds since the Unix epoch.
The toString() method converts that instant in time into a local time based on the default time zone. It's not that the Date value itself "has" a time zone - it's just toString() that uses the default one.
This data is supposed to be treated as 21:15 on all servers.
That suggests you want to use the Indian time zone in all servers, at least when converting the instant in time for display. Without knowing anything more about your application, that's all we can say... other than "don't use java.util.Date or java.util.Calendar; use the java.time classes instead". They're much better designed, and you're less likely to run into problems like this.
java.time
I recommend you use java.time, the modern Java date and time API, for your date and time work.
long scheduledTime = 1_602_258_300_000L;
Instant pointInTime = Instant.ofEpochMilli(scheduledTime);
System.out.println(pointInTime);
Output from this snippet will be the same on all servers in all time zones:
2020-10-09T15:45:00Z
Since you want 21:15, specify the time zone for India:
ZoneId serverTimeZone = ZoneId.of("Asia/Kolkata");
ZonedDateTime dateTime = pointInTime.atZone(serverTimeZone);
System.out.println(dateTime);
2020-10-09T21:15+05:30[Asia/Kolkata]
What went wrong?
The epoch is one point in time independent of time zone. so a count of milliseconds also denotes one point in time. In your case that point in time is Friday 9. October 2020 15:45:00 UTC. And at that point in time it was 21:15 in India and 11:45 on the East coast of North America. It’s a confusing trait of the outdated Date class that on one hand it represents just a point in time, on the other hand its toString method grabs the time zone setting of the JVM and uses it for rendering the string to be returned, thus giving you the false impression that you get different Date objects in different time zones when in fact they are equal.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Epoch & Unix Timestamp Conversion Tools where you can check what’s the equivalent of your milliseconds in UTC/GMT and in your own time zone.
As pointed by others you should now use the java.time package for working with time. If you look at the documentation of the toString() method of java.util.Date, it says that it coverts the Date object to a String of form:
EEE MMM d m dd hh:mm:ss zzz yyyy
It is like the following code is running in the background:
public String toString(){
Date date=this;
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getDefault()); //This line is important.
return simpleDateFormat.format(date);
}
Now, if you wanna format your Date object for a certain timezone you can do the same including setting the timezone:
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("IST"));
String dateStr = simpleDateFormat.format(date);
System.out.println("Date obj :" + dateStr);
I have a timestamp 2018-01-01 18:20:23.11 which is in UTC. I need to print this in a different format but retain the UTC timezone. However if I use SimpleDateFormat ("dd MMM YYYY yyyy kk:mm z"), it takes my current timezone and gives me 01 Jan 2018 18:20 EST. I want this to print 01 Jan 2018 18:20 UTC. Doing a Timezone.getTimeZone("UTC") converts this time to UTC (does a +4 to hours)which is not the desired result.
DateTimeFormatter originalFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SS");
DateTimeFormatter newFormatter
= DateTimeFormatter.ofPattern("dd MMM uuuu HH:mm z", Locale.ENGLISH);
String originalTimestamp = "2018-01-01 18:20:23.11";
String differentFormat = LocalDateTime.parse(originalTimestamp, originalFormatter)
.atZone(ZoneId.of("Etc/UTC"))
.format(newFormatter);
System.out.println(differentFormat);
This prints
01 Jan 2018 18:20 UTC
ZoneId.of("Etc/UTC") or ZoneOffset.UTC?
A possibly nerdy edit: I had first written .atZone(ZoneOffset.UTC) in the conversion. I usually use ZoneOffset.UTC to denote UTC and consider this the nice and idiomatic way of specifying it. However in the case of your code, this resulted in the zone being given as Z in the output where you had asked for UTC. Using ZoneId.of("Etc/UTC") instead gives you what you want. I can find no other way of making sure that the zone is formatted as UTC (save hardcoding UTC in the format pattern string, but that would be an ugly hack).
BTW ZoneId.of("Etc/UTC").normalized() returns ZoneOffset.UTC (at least on my Java 10, but I expect it to be the case always).
SimpleDateFormat vs. java.time
SimpleDateFormat is not only long outdated, it is also notoriously troublesome. I recommend you avoid it. It is correct, as you have observed, that it uses your JVM’s default time zone. There is a way to persuade it to do differently, but I would not bother.
java.time is the modern Java date and time API. It came out in 2014 as a replacement for the old and poorly designed date and time classes. IMHO it is so much nicer to work with.
Link: Oracle tutorial: Date Time explaining how to use java.time
I use the following SimpleDateFormat to parse a string,
SimpleDateFormat ft= new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy");
String notimeZone = ft.format(startDateTime);
Date date = ft.parse(notimeZone);
startDateTime is Date object with value in format "Thu Mar 06 12:27:55 IST 2014".
in notimeZone variable i get, Thu Mar 06 12:27:55 2014
I am expecting output in variable date as, Thu Mar 06 12:27:55 2014
But am getting same , Thu Mar 06 12:27:55 IST 2014.
How to remove the time zone from the date object.
Please help.
java.util.Date does not have a Timezone. It is not aware of TimeZone.
When you print, java picks up the default time zone.
Conceptually, you cannot have a date time without timezone. A date time has to be in one and only one zone. You may convert it to other zones, but it can never be without a zone.
If your business use case requires awareness of Time Zone, I would prefer to use Calendar class, or Joda Time. Avoid playing with Date class. If your business use cases do not require any time zone awareness, then go ahead with date. Assume all Date instances are in your default time zone.
Best thing is to use Calendar (or Joda Time). Calendar is a bit unintuitive but you will be able to get your job done. It is not without reason that Date class is mostly deprecated.
Initialize your Calendar with Timezone (check out available constructors). Use Dateformat to convert and show in UI in whatever time zone you wish to. I posted some code in your previous question. That might help you. I was stuck in a similar problem. I have dates in the DB in one time zone. To display them in another time zone on UI, you need to convert that datetime to another zone.
The following format gives
"EEE MMM dd HH:mm:ss z yyyy"->Thu Mar 06 13:18:02 IST 2014.
"EEE MMM dd HH:mm:ss yyyy" -> Thu Mar 06 13:18:02 2014.
Check if you gave a 'z' by mistake in the date format string.
For more details, Refer - http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
I need a date format (maybe SimpleDateFormat) that parses reliable the output I get when I call toString() on a Date object. Output on my german(!) system is: "Sun Dec 12 13:45:12 CET 2010", so it doesn't seem to honor locales, which seems to make it easy.
Anyone?
That format is specified in the Date#toString().
Converts this Date object to a String of the form:
dow mon dd hh:mm:ss zzz yyyy
So, in SimpleDateFormat pattern terms:
EEE MMM dd HH:mm:ss zzz yyyy
Unrelated to the problem, I wonder if it wasn't in first place a bad idea to use Date#toString() instead of SimpleDateFormat#format() to output dates. I would consider to fix it straight there.
BalusC gave you the correct format, you I'd say - don't. The toString() method must not be used for anything other than logging.
You can use SimpleDateFormat for both formatting and parsing.
TL;DR
Instant parsedBack = Instant.parse(Instant.now().toString());
System.out.println(parsedBack);
2019-05-30T08:36:47.966274Z
Use ISO 8601 and java.time
If your real goal is to serialize and deserialize a date and time (for data transfer or for persistence, for example), serialize to ISO 8601, the standard format for date and time data.
Skip the long outdated Date class. The modern Java date and time API known as java.time is so much nicer to work with. The class you need from it is probably Instant (this depends on your more exact requirements).
The two points go nicely hand in hand:
Instant i = Instant.now();
String s = i.toString();
Instant theSameInstant = Instant.parse(s);
The modern classes’ toString methods produce ISO 8601 format (e.g., 2018-01-11T10:59:45.036Z), and their parse methods read the same format back. So this snippet is all you need, and you get an instant equal to the first, with nanosecond precision.
If you cannot control the string you get, and you get the result from Date.toString(), the format pattern string in BalusC’s answer works with java.time too:
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
Date d = new Date();
String s = d.toString();
Instant nearlyTheSameInstant = ZonedDateTime.parse(s, dtf).toInstant();
Some warnings, though:
Milliseconds from the original Date are lost since they are not in the string, leading to an inaccuracy of up to 999 milliseconds (which was why I named the variable nearlyTheSameInstant).
The era from the original Date is not in the string either. So if your original Date was in year 44 BCE, you will get the corresponding date in year 44 CE (AD) (in which case the variable name nearlyTheSameInstant was a lie anyway).
The time zone abbreviation in the string is often (most often?) ambiguous, so there is a great risk of getting the wrong time zone and hence a wrong time. To make matters worse, an ambiguous time zone abbreviation will be interpreted differently on different JVMs
It’s essential to provide a locale. Otherwise the JVM’s default locale will be used, and if it’s not English, parsing will fail. In the worst case you will see your code running fine for many years and suddenly it will break when one day someone runs it on a computer or device with a different locale setting. I use Locale.ROOT for “the locale neutral locale” or “don’t apply any locale specific processing”. It seems to be the correct approach here.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
you can use jdk8's DateTimeFormatter
DateTimeFormatter dtf
= DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT);
Date d = new Date();
String s = d.toString();
Instant nearlyTheSameInstant = ZonedDateTime.parse(s, dtf).toInstant();
Date nearlyTheSameInstantDate = Date.from(nearlyTheSameInstant);
DateTime dateTime = new DateTime(nearlyTheSameInstantDate);
String str = dateTime.toString("yyyy/MM/dd");
System.out.println("str = " + str);
I have a requirement of sending the calendar object with time zone, like Tue Mar 03 13:43:00. I know that Calendar object always return with Tue Mar 03 13:43:00 CST 2009, so how can I create it without the time zone?
You can use the DateFormat class (more specifically the SimpleDateFormat concrete subclass) to format the date however you want. Here is an example:
Calendar cal = Calendar.getInstance();
DateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss");
String dateString = format.format(cal.getTime()));
Prints:
Fri Mar 06 15:50:26
It's not possible to have a Calendar object without a TimeZone. Really, that doesn't make any sense - Calendar seeks to represent "a specific instant in time" - how can you represent an instance in time without knowing what TimeZone the time you are representing is in?
So, there's a few things to consider:
You say you need to pass this Calendar to some other component - does that API care what TimeZone the Calendars are in? Will that API ignore the TimeZone setting, use it to calculate a time difference against it's own local timezone, etc.?
Are you aware that you can easily change the TimeZone of a Calendar instance?
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getDefault()); // returns default TimeZone for your system
cal.setTimeZone(TimeZone.getTimeZone("EST")); // Eastern Standard Time
cal.setTimeZone(TimeZone.getTimeZone("Asia/Kolkota"); // UTC+5:30
Javadocs on Calendar, TimeZone.
You can use a simple Date object using;
Date d = new Date();
Date d2 = calendar.getTime();
Or the even simpler, time in milli-seconds.
long time = System.currentTimeMillis();
To avoid confusion over time zones, I suggest storing and setting all date/times as a standard time zone, such as GMT+0 and then converting the time for display etc as required.
Upon rereading your question and your comments on my other answer, it sounds like the problem is that you want the result to be a Calendar object, not a String. So it isn't a question of formatting the String representation of the Calendar.
In this case, what you are asking is technically impossible: all implementations of the Calendar class exist to represent an internal time, which is stored as the number of milliseconds since the Unix epoch (Jan 1 1970 00:00 GMT). The time "5:00 am" in Boston is different from "5:00 am" in Seattle--they have different internal timestamps.
Your best bet would be to use the same time zone everywhere, and probably GMT makes the most sense for that purpose:
cal.setTimeZone(TimeZone.getTimeZone("GMT+0000"));