Java datetime inconsistent between local application and Jenkins deployment - java

I am receiving a date time as a string into my application. There I need to extract the hour so it can be written to a file along with some other stuff. For my unit test the string will look like this
2019-10-26T00:00:00+01:00
The code I use to extract the hour is this
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
public static int extractHour(String dateInString) {
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ");
DateTime dateTime = formatter.parseDateTime(dateInString);
return dateTime.getHourOfDay();
}
My test passes locally with expected hour 00 and actual hour 00 but when I deploy through Jenkins the actual hour is showing as 23 and my expected is 00.

When you don’t instruct Joda-Time otherwise, formatter.parseDateTime() parses into a DateTime in your default time zone.
So if for example your local time zone is set to Europe/Dublin or Europe/London, the result of parsing will be a DateTime of 2019-10-26T00:00:00.000+01:00 (because October 26 was the last day with summer time (DST) in those time zones) and the hour of day will be 0 as you expected. And if the time zone setting of your Jenkins server is UTC — that’s pretty commonplace — your string is parsed into 2019-10-25T23:00:00.000Z there. And the hour of day will be 23, obviously.
If you want the offset from your string to be retained in the DateTime, the fix is:
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ")
.withOffsetParsed();
However, think twice. The hour of day only makes sense relative to the offset. In the night where summer time ended you could have strings of 2019-10-27T01:00:00+01:00 and an hour later when the clocks are changed, 2019-10-27T01:00:00+00:00. Both strings have hour of day of 1, but there’s an hour between them. Do you really want the same result? What if one day you get a string of 2020-02-16T00:00:00-05:00?
Edit:
What would you suggest rather than using withOffsetParsed()[?]
I first of all suggest that you decide what result you want for strings with different offsets, for example 2020-03-06T18:00:00+05:30 and 2020-04-18T04:00:00-08:00. You ought to know that better than I.
A generally recommended practice for handling time across offsets is to handle everything in UTC. This can be a special case of the solution you already found and mentioned in a comment, specifying time zone on the formatter:
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ")
.withZoneUTC();
This guarantees a consistent result, apparently the same result that you already got on the Jenkins server. So 23 in your example.
PS If this is new code, you probably shouldn’t use Joda-Time. The Joda-Time homepage says:
Note that Joda-Time is considered to be a largely “finished” project.
No major enhancements are planned. If using Java SE 8, please migrate
to java.time (JSR-310).

Related

ZonedDateTime to Instant is right in Java 8 but off by 1h in Java 9+ [duplicate]

I am migrating an application from jdk 8 to 11 and I can see ZonedDateTime change is behavior about daylight saving time.
JDK8
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
output:
2037-05-10T19:15+02:00[Europe/Paris]
JDK11/12
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
2037-05-10T20:15+02:00[Europe/Paris]
Can someone explain to me why did they change this behavior ?
Best regards,
It’s a known bug in Java 8: JDK-8066982
I believe that what you are experiencing in Java 8 is really this bug: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition. The bug title doesn’t tell the whole story. The real issue is that in Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME (which is implicitly used by the one-arg ZonedDateTime.parse that you use) ignores the offset if a time zone ID is included in the parsed string. This in combination with a time zone database that disagrees with your string about the offset used in Paris in October 2037 causes a moment in time to be parsed that conflicts with the offset in the string.
The bug is fixed in Java 9. So in Java 9, 10 and 11, since the same disagreement about offset is still there, the moment parsed is based on the offset of the string. It is then converted to the time zone from the string using the rules from the time zone database. This causes the offset to be changed from +01:00 to +02:00 and the hour of day correspondingly from 19:15 to 20:15. I agree with Java 9+ that this is the correct behaviour.
Don’t use ZonedDateTime for far-future dates
Your problem is also partly caused by using ZonedDateTime for a future date. This is only recommended for the very near future where we assume that no zone rules are changed. For a date and time in 2037, you should either use an Instant if you know the moment in time, or a LocalDateTime if you know just the date and time of day. Only when the time draws near and you trust that your Java installation has got the last time zone updates, convert to a ZonedDateTime.
As has been discussed in the comments, we probably don’t know the correct UTC offset for Paris in October 2037 yet. It seems that EU is likely to abandon summer time (DST) from 2021, and as far as I know, the French politicians have not yet decided what the time will be in France after that.
What if we wanted the time of day from the string?
To get the time from the string (19:15), parse into a LocalDateTime:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
Output is (run on Java 11):
Date and time from string: 2037-05-10T19:15
In case you wanted the full Java 8 behaviour on a later Java version — as I mentioned, it’s not recommended, you shouldn’t use ZonedDateTime here:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
Time from string in zone from string: 2037-05-10T19:15+02:00[Europe/Paris]

ZonedDateTime change behavior jdk 8/11

I am migrating an application from jdk 8 to 11 and I can see ZonedDateTime change is behavior about daylight saving time.
JDK8
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
output:
2037-05-10T19:15+02:00[Europe/Paris]
JDK11/12
ZonedDateTime parse = ZonedDateTime.parse("2037-05-10T19:15:00.000+01:00[Europe/Paris]");
System.out.println(parse);
2037-05-10T20:15+02:00[Europe/Paris]
Can someone explain to me why did they change this behavior ?
Best regards,
It’s a known bug in Java 8: JDK-8066982
I believe that what you are experiencing in Java 8 is really this bug: ZonedDateTime.parse() returns wrong ZoneOffset around DST fall transition. The bug title doesn’t tell the whole story. The real issue is that in Java 8 DateTimeFormatter.ISO_ZONED_DATE_TIME (which is implicitly used by the one-arg ZonedDateTime.parse that you use) ignores the offset if a time zone ID is included in the parsed string. This in combination with a time zone database that disagrees with your string about the offset used in Paris in October 2037 causes a moment in time to be parsed that conflicts with the offset in the string.
The bug is fixed in Java 9. So in Java 9, 10 and 11, since the same disagreement about offset is still there, the moment parsed is based on the offset of the string. It is then converted to the time zone from the string using the rules from the time zone database. This causes the offset to be changed from +01:00 to +02:00 and the hour of day correspondingly from 19:15 to 20:15. I agree with Java 9+ that this is the correct behaviour.
Don’t use ZonedDateTime for far-future dates
Your problem is also partly caused by using ZonedDateTime for a future date. This is only recommended for the very near future where we assume that no zone rules are changed. For a date and time in 2037, you should either use an Instant if you know the moment in time, or a LocalDateTime if you know just the date and time of day. Only when the time draws near and you trust that your Java installation has got the last time zone updates, convert to a ZonedDateTime.
As has been discussed in the comments, we probably don’t know the correct UTC offset for Paris in October 2037 yet. It seems that EU is likely to abandon summer time (DST) from 2021, and as far as I know, the French politicians have not yet decided what the time will be in France after that.
What if we wanted the time of day from the string?
To get the time from the string (19:15), parse into a LocalDateTime:
String zdtString = "2037-05-10T19:15:00.000+01:00[Europe/Paris]";
LocalDateTime dateTime
= LocalDateTime.parse(zdtString, DateTimeFormatter.ISO_ZONED_DATE_TIME);
System.out.println("Date and time from string: " + dateTime);
Output is (run on Java 11):
Date and time from string: 2037-05-10T19:15
In case you wanted the full Java 8 behaviour on a later Java version — as I mentioned, it’s not recommended, you shouldn’t use ZonedDateTime here:
TemporalAccessor parsed = DateTimeFormatter.ISO_ZONED_DATE_TIME.parse(zdtString);
LocalDateTime dateTime = LocalDateTime.from(parsed);
ZoneId zone = ZoneId.from(parsed);
ZonedDateTime java8Zdt = dateTime.atZone(zone);
System.out.println("Time from string in zone from string: " + java8Zdt);
Time from string in zone from string: 2037-05-10T19:15+02:00[Europe/Paris]

How to find epoch format current time of GMT using java

I have written below code which is running, and giving output. But I'm not sure It's a right one.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
sdf.setTimeZone(TimeZone.getTimeZone("GMT-7"));
String value = sdf.format(date);
System.out.println(value);
Date date2 = sdf.parse(value);
long result = date2.getTime();
System.out.println(result);
return result;
The above code what I'm trying is, I just need to get the current time of GMT time zone and convert it as epoch format which is gonna used in Oracle db.
Can someone tell me that format, and the above code is right?
First, you should not store time since the epoch as a timestamp in your database. Look into the date-time datatypes that your DMBS offers. In Oracle I think that a date column will be OK. For most other DBMS you would need a datetime column. timestamp and timestamp with timezone may be other and possibly even sounder options depending on your exact requirements.
However, taking your word for it: Getting the number of milliseconds since the epoch is simple when you know how:
long millisecondsSinceEpoch = System.currentTimeMillis();
System.out.println(millisecondsSinceEpoch);
This just printed:
1533458641714
The epoch is defined in UTC, so in this case we need to concern ourselves with no other time zones.
If you needed seconds rather than milliseconds, it’s tempting to divide by 1000. However, doing your own time conversions is a bad habit since the libraries already offers them, and using the appropriate library methods gives clearer, more explanatory and less error-prone code:
long secondsSinceEpoch = Instant.now().getEpochSecond();
System.out.println(secondsSinceEpoch);
1533458641
You said:
I just need to get the current time of GMT time zone…
Again taking your word:
OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(currentTimeInUtc);
long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli();
System.out.println(millisecondsSinceEpoch);
2018-08-05T08:44:01.719265Z
1533458641719
I know that GMT and UTC are not exactly the same, but for most applications they can be (and are) used interchangeably.
Can someone tell me (if) the above code is right?
When I ran your code just now, its output agreed with mine except the milliseconds were rounded down to whole thousands (whole seconds):
1533458641000
Your code has some issues, though:
You are using the old, long out-dated and poorly designed classes SimpleDateFormat, Date and TimeZone. The first in particular has a reputation for being troublesome. Instead we should use java.time, the modern Java date and time API.
Bug: In your format pattern string you are using lowercase hh for hour of day. hh is for hour within AM or PM, from 1 through 12, so will give you incorrect results at least half of the day. Uppercase HH is for hour of day.
Don’t use GMT-7 as a time zone. Use for example America/Los_Angeles. Of course select the time zone that makes sense for your situation. Edit: You said:
I just want to specify the timezone for sanjose. GMT-7 is refer to
sanjose current time.
I believe many places are called San Jose. If you mean San Jose, California, USA, you are going to modify your program to use GMT-8 every time California goes back to standard time and opposite when summer time (DST) begins?? Miserable idea. Use America/Los_Angeles and your program will work all year.
Since you ask for time in the GMT time zone, what are you using GMT-7 for at all?
There is no point that I can see in formatting your Date into a string and parsing it back. Even if you did it correctly, the only result you would get would be to lose your milliseconds since there are no milliseconds in your format (it only has second precision; this also explained the rounding down I observed).
Links
Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API.
San Jose, California on Wikipedia
Why not use Calendar class?
public long getEpochTime(){
return Calendar.getInstance(TimeZone.getTimeZone("GMT-7")).getTime().getTime()/1000; //( milliseconds to seconds)
}
It'll return the current Date's Epoch/Unix Timestamp.
Based on Harald's Comment:
public static long getEpochTime(){
return Clock.system(TimeZone.getTimeZone("GMT-7").toZoneId() ).millis()/1000;
}
Here is a solution using the java.time API
ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.of("GMT-7"));
long millis = zdt.toInstant().toEpochMilli();

android java time

i'm building an android application which have a chat.
in this chat i each message to have its time sent signature.
my question is as follow:
lets say that the time in my country is X. my friend is abroad and his time is X minus 7 hours.
i'm sending him a message at 16:00 local time.
i want to avoid the situation that he will get at 09:00 a message which it signature will be 16:00 (which is a time in future if you're looking in the eyes of that friend in his country).
is there a way that in my phone the message will be written as 16:00 and in his phone it will be written as 09:00 ? i there a way to convert a time to a local time ?
System.currentTimeMillis() does give you the number of milliseconds since January 1, 1970 00:00:00 UTC. Date object does not save your local timezone.
You can use DateFormats to convert Dates to Strings in any timezone:
DateFormat df = DateFormat.getTimeInstance();
df.setTimeZone(TimeZone.getTimeZone("gmt"));
String gmtTime = df.format(new Date());
linked response
You should keep all time communications using UTC time. Then localize it for display based on the devices current timezone setting.
Use a long to save your time information as milliseconds since "epoch" (which is January 1, 1970, 00:00:00 GMT). It can be retreived with the Date.getTime() method and new Date objects are easily created using the Date(long millis) constructor. The Date objects are then displayed using the local timezone settings on each device.
EDIT:
Epoch is a defined point in time which is expressed differently in different time zones: 1970-01-01 00:00:00 GMT but
1969-12-31 19:00:00 EST. The timestamp is just the number of milliseconds elapsed since that time. So, for example the timestamp 1341169200 corresponds to 2012-07-01 19:00:00 GMT and 2012-07-01 14:00:00 EST.
You will need to save the time zone which your message will be saved in, and transfer it (or send the unix epoch time) and then on the other side make sure you read it in with the Locale time (using the Android documentation for things like http://developer.android.com/reference/java/util/Calendar.html can help).
Take a look at the answer over here:
https://stackoverflow.com/a/6094475/346232
You need to change the time to UTC and then convert on the device to the timezone.
Avoid java.util.Date/.Calendar
The java.util.Date/.Calendar classes bundled with Java (and Android) are notoriously troublesome, flawed in both design and implementation.
Joda-Time
The Joda-Time library is the way to go. This library inspired the java.time package now built into Java 8 (not available on Android).
UTC
As other answers suggested, the best practice (generally) is to keep your business logic and data storage/communication in UTC time zone (which some think of as no time zone or an "anti" time zone). Adjust to a specific time zone only when expected by the user or data-consumer.
Time Zone
The DateTime class in Joda-Time represents a date-time value along with an assigned time zone.
Note that it is best to specify a time zone in all your operations. Otherwise you will be implicitly relying on the JVM’s current default time zone. This is risky because that zone can change – even at runtime at any moment by any code in any thread of any app running within your app’s JVM. And use proper time zone names, never the 3-4 letter codes.
Example Code
Example code in Joda-Time 2.7.
DateTime sent = DateTime.now( DateTimeZone.getDefault() ) ;
DateTime sentUtc = nowMine.withZone( DateTimeZone.UTC ) ; // Generally, use this for your work, including communicating to other threads and apps and such.
When ready to display to the other user, adjust to the expected time zone.
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" ) ; // Or DateTimeZone.getDefault() if you want to rely on their JVM’s current default. To be absolutely sure of expected time zone, you really must ask the user.
DateTime sentMontréal = sentUtc.withZone( zone );
To generate a textual representation of those date-time objects, search the many Questions and Answers on StackOverflow.com on that subject. Search for terms like "joda" and "DateTimeFormatter" and "DateTimeFormat".

Convert time represented as String containing timezone names ('z') to UTC time

I want to convert Strings like "20000603163334 GST" or "20000603163334 -0300" to UTC time. The problem is that time zones in my strings can be 'general time zones', I mean they can be strings as CET, GST etc. etc. And I don't know how to convert these ones.
Because of these string time zones I can not use Joda Time's DateTimeFormat.forPattern("yyyyMMddhhmmss z").withZone(DateTimeZone.UTC);, because according to the documentation: "Time zone names ('z') cannot be parsed".
So, one question I have is if you know a method to go around this limitation in Joda Time? I would prefer to use Joda Time, if possible, instead of the standard Java API.
Another in which I thought I can solve this problem with time zone's names is to use the Java's SimpleDateFormat.
So I make something like:
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddhhmmss z");
//f.setTimeZone(TimeZone.getTimeZone("UTC"));
f.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("UTC")));
Date time = f.parse("20000603163334 GST");
The SimpleDateFormat parses the String (I don't care here about the problem that there are multiple time zones with the same name - what this class parses it's good for me).
The problem is that I don't know how to convert it from here to UTC. How can I do this?
The fact that I set the f's time zone to UTC (in both the two ways from above) doesn't help. I hope someone can help me fix this, I read a lot of questions and answers on this theme here, on stackoverflow, but I haven't found a solution yet.
I found two solutions to your problem. The first was to set the default time zone to UTC:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
I'm not sure what other side effect this might have.
The second solution I found was to use a different SimpleDateFormat for output.
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddhhmmss z");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
Date time = f.parse("20000603163334 GST");
System.out.println(time);
System.out.println("(yyyyMMddhhmmss z): " + f.format(time));
SimpleDateFormat utc = new SimpleDateFormat("yyyyMMddhhmmss z");
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("(yyyyMMddhhmmss z): " + utc.format(time));
Using two SimpleDateFormat objects allowed the output to be put in UTC Time. Here is the output from running this code:
Sat Jun 03 08:33:34 EDT 2000
(yyyyMMddhhmmss z): 20000603043334 GST
(yyyyMMddhhmmss z): 20000603123334 UTC
Here may be the reason why Joda does not support 3 letter zone ids. This is from the TimeZone ( http://download.oracle.com/javase/6/docs/api/java/util/TimeZone.html ) JavaDoc. As far as Joda goes, I didn't see a workaround, but I'm not very familiar with that library.
Three-letter time zone IDs For
compatibility with JDK 1.1.x, some
other three-letter time zone IDs (such
as "PST", "CTT", "AST") are also
supported. However, their use is
deprecated because the same
abbreviation is often used for
multiple time zones (for example,
"CST" could be U.S. "Central Standard
Time" and "China Standard Time"), and
the Java platform can then only
recognize one of them.

Categories

Resources