I run the following code:
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");
try{
Date date = sdf.parse("03-28-2003 01:00:00");
System.out.print(date.toString());
}
catch(Exception e){
//do something
}
The result of the parsing is this date: 2003-03-28T02:00:00.000+0300
One hour is added.
When I change the year/day/hour to any other valid number, I get the correct time, no extra hour is added. If I only change the minutes or the seconds I still get the added hour.
Can anyone tell me why this happens?
EDIT:
This is related to when daylight saving time is applied in the timezone my program runs on- UTC+02:00.
In this timezone the clock changed on 2003-03-28. that's why an hour was added, as it was suggested by the comments and answer below.
I used the code suggested in the answer to parse my date and the parsing worked! The date is parsed correctly, the extra hour isn't added.
Finding out exactly what your code does is complicated by the fact that not only SimpleDateFormat.parse() may depend on the default time zone of the computer (and does in this case where the pattern does not include time zone), also Date.toString() depends on the default time zone. However, I understand that you want to interpret the date string in UTC, so I will concentrate on getting the parsing right and not worry so much about what’s printed.
Feek is correct in the comment that setting the time zone of the SimpleDateFormat to UTC will get you what you want, for example:
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
With this line added before try I get this output on my computer:
Fri Mar 28 02:00:00 CET 2003
2 am. CET agrees with 1 UTC, so now the parsing is correct.
Allow me to add that if you can use the Java 8 date and time classes, I find the corresponding code somewhat clearer:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss");
LocalDateTime dateTime = LocalDateTime.parse("03-28-2003 01:00:00", formatter);
OffsetDateTime utcDateTime = dateTime.atOffset(ZoneOffset.UTC);
System.out.println(utcDateTime);
The point is not that it’s shorter, but that you don’t get easily in doubt about what it does and don’t easily get time zone or DST problems. An added benefit is that the output is also as expected:
2003-03-28T01:00Z
Now it’s evident that the time is correct (Z means Z or Zulu or UTC time zone, it’s got more than one name).
If for some reason you absolutely need an oldfashioned java.util.Date object, that is not difficult:
Date date = Date.from(utcDateTime.toInstant());
This gives the same date as we got from sdf.parse() with UTC time zone.
Related
I have a date time that I am first converting to local time, followed by a conversion to another time zone. The first conversion works with no issue however the second conversion is ignored. What is the issue?
String input = "2020-05-20 01:10:05";
SimpleDateFormat localFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
localFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
try {
Date date = localFormat.parse(input);
System.out.println(date); //Wed May 20 01:10:05 PDT 2020 <--- Logs this (Expected)
SimpleDateFormat estFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
estFormat.setTimeZone(TimeZone.getTimeZone("America/New_York"));
String newDate = estFormat.format(date);
System.out.println(newDate); //2020-05-20 04:10:05 <--- Logs this (Expected)
Date dateEst = estFormat.parse(newDate);
System.out.println(dateEst); //Wed May 20 01:10:05 PDT 2020 <--- Logs this (Unexpected) Should be Wed May 20 04:10:05 EST 2020
}catch (Exception e){
e.printStackTrace();
}
It seems like the second estFormat.parse() is ignored when trying to convert to America/New_York time zone.
java.time
I warmly recommend that you use java.time, the modern Java date and time API, for your date and time work. Once you get used to the slightly different mindset, you will likely also find the resulting code clearer and more natural to read. Let’s first define the constant stuff as constants:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
private static final ZoneId FROM_ZONE = ZoneId.of("America/Los_Angeles");
private static final ZoneId TO_ZONE = ZoneId.of("America/New_York");
With these we can do our work:
String input = "2020-05-20 01:10:05";
ZonedDateTime fromDateTime
= LocalDateTime.parse(input, FORMATTER).atZone(FROM_ZONE);
System.out.println("From: " + fromDateTime);
ZonedDateTime toDateTime = fromDateTime.withZoneSameInstant(TO_ZONE);
System.out.println("To: " + toDateTime);
String formatted = toDateTime.format(FORMATTER);
System.out.println("Formatted: " + formatted);
Output is:
From: 2020-05-20T01:10:05-07:00[America/Los_Angeles]
To: 2020-05-20T04:10:05-04:00[America/New_York]
Formatted: 2020-05-20 04:10:05
Edit:
How would I get it to have EST 2020 at the end?
A good option for most purposes is to use a localized formatter:
private static final DateTimeFormatter TARGET_FORMATTER
= DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.US);
Like this:
String formatted = toDateTime.format(TARGET_FORMATTER);
Formatted: May 20, 2020 at 4:10:05 AM EDT
A detail, we didn’t get EST at the end because New York and most of the East coast of Northern America uses summer time (DST) and hence is on Eastern Daylight time, EDT, in May.
What went wrong?
It seems that you were expecting your Date object to carry the time zone of the formatter that parsed it, America/New_York. An old-fashioned Date object cannot do that. It’s just a dumb point in time without any time zone or other additional information. What confuses many is that its toString method uses the default time zone of the JVM to render the string returned, thus giving the false impression of a time zone being present. In contrast the modern ZonedDateTime, as the name says, does hold a time zone.
Link
Oracle tutorial: Date Time explaining how to use java.time.
First, don't use Date unless you're maintaining legacy code.
Second, SimpleDateFormat parsed the date correctly. You're not using estFormat to format the date as you did in the previous (correct) example. Try:
System.out.println(estFormat.format(dateEst));
This is a good overview of the different types of date-related entities in Java and is worthwhile reading. Here's an excerpt you may find useful:
The java.util.Date has no concept of time zone, and only represents the number of seconds passed since the Unix epoch time – 1970-01-01T00:00:00Z. But, if you print the Date object directly, the Date object will be always printed with the default system time zone.
I had noticed one thing the type of the first variable is String and the type of the Second variable is Date. So probably type conversion is creating a problem here. There is nothing like ignoring a second time.
I have a Date with the actual time of my system (I live in Spain). I need to change it to UTC-1, but it doesn't matter if I write "UTC-1" or "UTC-2", it always gives me the same time less 2 hours, I mean:
My system hour (time_utc): 11:00 13/04/2021
Try UTC-1 (time): 09:00 13/04/21
Try UTC-2 (time): 09:00 13/04/21
I have this code:
Date time_utc = new Date();
DateFormat convertidor = new SimpleDateFormat("yyyy-MM-dd HH:00:00.000");
convertidor.setTimeZone(TimeZone.getTimeZone("UTC-1"));
time = convertidor.format(time_utc);
Why it doesn't work? Can anyone helps me? Thanks a lot!
¡Hola!
You can do that in a pretty short way using java.time (if you are allowed and willing to do so).
There are special classes that represent a moment in time in different time zones of offsets. One of them is an OffsetDateTime, see this example:
public class Main {
public static void main(String[] args) {
// create one of your example date times in UTC
OffsetDateTime utcOdt = OffsetDateTime.of(2021, 4, 13, 11, 0, 0, 0, ZoneOffset.UTC);
// and print it
System.out.println(utcOdt);
/*
* then create another OffsetDateTime
* representing the very same instant in a different offset
*/
OffsetDateTime utcPlusTwoOdt = utcOdt.withOffsetSameInstant(ZoneOffset.ofHours(2));
// and print it
System.out.println(utcPlusTwoOdt);
// do that again to see "the other side" of UTC (minus one hour)
OffsetDateTime utcMinusOneOdt = utcOdt.withOffsetSameInstant(ZoneOffset.ofHours(-1));
// and print that, too.
System.out.println(utcMinusOneOdt);
}
}
It outputs the following three lines:
2021-04-13T11:00Z
2021-04-13T13:00+02:00
2021-04-13T10:00-01:00
As you can see, the time of day is adjusted according to the offset.
The output could be formatted in your desired style if needed (this currently just uses the toString() method of OffsetDateTime).
UPDATE
You can achieve the output formatted as desired by defining the pattern as uuuu-MM-dd HH:mm when using a java.time.format.DateTimeFormatter.
Just add the following lines to the example above:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm");
System.out.println(utcOdt.format(dtf));
System.out.println(utcPlusTwoOdt.format(dtf));
System.out.println(utcMinusOneOdt.format(dtf));
This would then output
2021-04-13 11:00
2021-04-13 13:00
2021-04-13 10:00
And if you really want fix zeros for seconds and millis, then create your DateTimeFormatter like this:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:00.000");
which will cause output like this:
2021-04-13 11:00:00.000
2021-04-13 13:00:00.000
2021-04-13 10:00:00.000
As a supplement to the good answer by deHaar:
As Matt Johnson-Pint already asked, do you need to convert to a different time zone? This would be the most typical. If so, use that time zone, not just a UTC offset of -1. By all probability that time zone has used other offsets in the past and may well do so in the future. So -01:00 isn’t safe. A real time zone ID like Atlantic/Cape_Verde is safer.
You don’t need to go through the current time in your own time zone and convert. java.time can directly give you the current time in another time zone or at a specific UTC offset.
java.time can also truncate a time to whole hours.
So for example:
DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS");
ZoneId zone = ZoneId.of("Atlantic/Cape_Verde");
ZonedDateTime nowInCaboVerde = ZonedDateTime.now(zone);
System.out.println(nowInCaboVerde);
System.out.println(nowInCaboVerde.truncatedTo(ChronoUnit.HOURS)
.format(formatter));
Output:
2021-04-14T03:12:28.272010-01:00[Atlantic/Cape_Verde]
2021-04-14 03:00:00.000
PS Cabo Verde/Cape Verde was at offset -02:00 until 1975.
What went wrong in your code?
This is how confusingly the old TimeZone class behaves and one of the reasons why you should never use it: When given a time zone ID that it does not recognize, it returns GMT and pretends all is well. UTC-1 is not a recognized time zone ID. In case it didn’t make sense to refer to a real time zone and you needed the offset -01:00 from UTC, you might have used GMT-1 or GMT-01:00. Yes, TimeZone refers to UTC as GMT even though they are not strictly speaking the same.
I wanted to get the LocalDateTime in GMT so wrapped it with ZonedDateTime.
But gmtZoneTime is returned in the following format: 2019-10-29T00:00Z[GMT] While I need it to be: 2019-10-29T00:00:00.000+0000
How should I properly convert localDateTime into the GMT ZonedDateTime?
val currentDate:LocalDate = java.time.LocalDate.now
val localDateTime: LocalDateTime = currentDate.atStartOfDay
val gmtZoneTime: ZonedDateTime = localDateTime.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("GMT"))
You need to format the ZonedDateTime.
First approach would be to use predefined formatter like: java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME, however for GMT it shows 'Z' instead of '+0000' (default behaviour, other offsets are displayed like '+0100' etc.)
So the second one would be to create your own formatter like:
java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ")
and then use it to format ZonedDateTime like gmtZoneTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"))
so you get a result like:
2019-10-28T23:00:00+0000
First your code is incorrect. When I ran it in my time zone (Europe/Copenhagen) just now, I got
2019-10-29T23:00Z[GMT]
I don’t think you intended 23:00 in GMT.
Second you may think of GMT or UTC as an offset (of zero from UTC), so it is more correct to use an OffsetDateTIme than a ZonedDateTime for the time. This also eliminates your unwanted suffix. In Java (it’s all I can write):
LocalDate currentDate = LocalDate.now(ZoneOffset.UTC);
OffsetDateTime gmtZoneTime = currentDate.atStartOfDay(ZoneOffset.UTC)
.toOffsetDateTime();
System.out.println(gmtZoneTime);
Output when running just now:
2019-10-30T00:00Z
Edit: You can safely regard UTC and GMT as synonymous since java.time does that (even though strictly speaking they may differ by up to a second).
I assumed you also wanted the date in UTC, so passed this as argument to LocalDate.now(). If you want the date in some other time zone, pass that time zone to LocalDate.now() so that it is clear from the code what you get.
If you want that specific format in your question, pezetem is correct in the other answer that you need to format into a string:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSxx");
String formattedGmtTime = gmtZoneTime.format(formatter);
System.out.println(formattedGmtTime);
2019-10-30T00:00:00.000+0000
It seems wordy to me, though. I’d at least leave out the milliseconds since we know they are 0, probably the seconds too. Said without knowing your exact business case.
Link: Difference between UTC and GMT
I try to convert a string into a datetime:
String dateString = "2015-01-14T00:00:00-04:00";
DateTimeFormatter df = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ");
DateTime dt = df.parseDateTime(dateString);
If I display dt.toDate()
I get: Tue Jan 13 23:00:00 EST 2015
So there is a time problem.
Without the DateTimeFormatter, I get the same issue.
It's getting the correct value - basically 4am UTC, which is midnight in a UTC offset of -04:00 (as per the original text), or 11pm on the previous day for EST (as per the displayed result).
The problem is that you're using java.util.Date.toString(), which always returns the date in the system time zone. Note that a java.util.Date only represents an instant in time - it has no notion of a time zone itself, so its toString() method just uses the system default.
If you want to retain the time zone information (or in this case, the offset from UTC information - you don't have a full time zone) then stick to DateTime instead of converting to Date. Ideally, avoid java.util.Date/java.util.Calendar entirely. Stick to Joda Time and/or java.time.*.
I do not understand why MutableDateTime.setDate() is setting the time to "yesterday" (see the log timestamp hours - it is 20:28). Is this timezone related? Do I need to set something on the formatter?
I would expect that after calling setDate with "10/27/2010", the date would be the same as the parsed date 00:00 EDT 10/27/10, instead of 20:28 EDT 10/26/10. This is 24 hours ago from "now".
What am I missing here, or how should I edit the code to get the desired result? I am new to Joda Time, and would like to solve this mystery.
DateTimeFormatter dateFormatterJ = DateTimeFormat.forPattern("MM/dd/yyyy");
DateTimeFormatter timestampFormatJ = DateTimeFormat.forPattern("HH:mm zzz MM/dd/yy");
MutableDateTime startDate = new MutableDateTime();
log.info("parsed date " +
timestampFormatJ.print(dateFormatterJ.parseMutableDateTime(startDateString)));
startDate.setDate((dateFormatterJ.parseMutableDateTime(startDateString)));
log.info("startDate: " + timestampFormatJ.print(startDate));
In this case, startDateString is simply "10/27/2010".
here is the log output:
10-27 20:28:55 INFO parsed date: 00:00 EDT 10/27/10
10-27 20:28:55 INFO startDate: 20:28 EDT 10/26/10
Thanks
The simple answer would be, because the javadoc says so.
public void setDate(ReadableInstant
instant)
Set the date from another
instant. The time part of this object
will be unaffected.
Parameters:
instant - an instant to copy the date
from, time part ignored
Throws:
IllegalArgumentException - if the
object is invalidobject is invalid
When Joda says 'Date' it means the human meaning of the word Date. "The year-month-day portion of this value", not the logical equivalent of a java.util.Date. (the whole point of joda being to introduce some natural, sensible, semantics to handling date and time.)
EDIT:
To answer your 'how to fix' question, simply do:
MutableDateTime startDate = new MutableDateTime(dateFormatterJ.parseMutableDateTime(startDateString));
Or else manually zero out the time portions of course.
EDIT 2: Hmm, I apparently did not read carefully enough, this is only half of the answer. Will check.
EDIT 3: well this bugged me so much that I took a minute to look for it.
public void setDate(final ReadableInstant instant) {
long instantMillis = DateTimeUtils.getInstantMillis(instant);
Chronology instantChrono = DateTimeUtils.getInstantChronology(instant);
DateTimeZone zone = instantChrono.getZone();
if (zone != null) {
instantMillis = zone.getMillisKeepLocal(**DateTimeZone.UTC**, instantMillis);
}
setDate(instantMillis);
}
For some reason, it's rolling your absolute time forward into UTC before setting the date. So you give it 10/27/2010 00:00 EDT and it sets the absolute magnitude of time to the number of milliseconds that represent 10/27/2010 00:00 UTC, which of course is only 6 or 7 PM the day before. Then it finds the EDT date value of that to be 10/26.
Couldn't say if that's somehow intended or if it's a bug that's been there for 2 years or what.)
When parsing a string that does not contain a GMT offset or time-zone ID, you must do one of three things:
do nothing, and accept that the string is parsed in the default time zone
specify the time zone to parse in using withZone() on the formatter
use parseLocalDate() instead of parseMutableDateTime()
The last is the preferred solution, as it correctly parses the data that was actually input, which was a date without time, offset or zone.
Using parseLocalDate() in the test code correctly parses the date.