Joda Time not handling Day Light Saving transitions correctly - java

SCENARIO
I have two UTC timestamps that are 7 days apart:
val timestamp1: Long = 1600642800000L // GMT => Sunday, September 20, 2020 11:00:00 PM
val timestamp1: Long = 1601247600000L // GMT => Sunday, September 27, 2020 11:00:00 PM
The timezone used here is Africa/Casablanca.
val timeZone = DateTimeZone.forID("Africa/Casablanca")
Then, when I try to generate offset for both timestamps, it shows weird behavior:
timeZone.getOffset(timestamp1) // 3600000 milliseconds OR 1 hour
timeZone.getOffset(timestamp2) // 0 milliseconds
Africa/Casablanca currently has Day Light Saving enabled since May 24, 2020. It's clock was shifted on that day forward by 1 hr. So, currently it is at UTC + 1 hr. When Day Light Saving is not active, it is at UTC + 0.
QUESTION
So, how is the above behavior possible ? Shouldn't both timestamps generate the same 1 hr offset ?
Those two timestamps are only 7 days apart and no any Day Light Saving event happened between those timestamps.
I tried to reproduce similar behavior for other timezones but they always produce the same offset for those timestamps as expected.
Any insights on this behavior would be extremely helpful.

This issue was resolved when I upgraded Joda Time library to latest version 2.10.6 on my system with Java openjdk version "11.0.8". The reason this worked was that the latest version of Joda Time library had the latest timezone data with recent Day Light Saving rules. I realized that it is important to use the latest version and keep upgrading because Day Light Saving rules are political and can be changed by the governing body.
In addition, without doing any version upgrades, I also tested by writing the same code using native Java Time api. And by doing that also, this issue didn't appear.So, in the longer term, I would prefer using Java Time.

Related

while converting to data object, Moscow time is one hour behind

In my application while converting to date object i am always getting one hour behind time . this issue in happening only with moscow time zone.
below is code :
MutableDateTime mdt = new MutableDateTime(time);
mdt.setSecondOfMinute(0);
mdt.setMinuteOfDay(0);
mdt.toDate()
in above code mdt.todate() returning 5/30/2021 23:00 instead of 5/31/2021 00:00.
jdk version : "1.8.0_191"
Edit: Why does "June 6 00:00" after conversion mdt.toDate() become "May 31 23:00"?
Your surprising observation probably comes from an old Joda-Time version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. If Joda-Time “believes” that this is still the case, it sets your MutableDateTime to something like 2021-06-01T00:00:00.000+04:00 with offset +04:00 instead of +03:00. This corresponds to 2021-05-31T20:00Z UTC where the correct point in time would have been 2021-05-31T21:00Z UTC. In other words, it’s an hour too early. Therefore you get a Date that is an hour too early too. Your Java 1.8 “knows” that Moscow is at offset +03:00 these days and therefore prints the time as Mon May 31 23:00:00 MSK 2021.
Solutions include:
Upgrade to a newer version of Joda-Time that has an up-to-date time zone database.
Build your Joda-Time from sources for the version that you are using only with a newer bundled time zone database. This is explained on the Joda-Time home page, see the second link below.
Original answer
Your surprising observation probably comes from an old Java version with an old time zone database where Europe/Moscow was at offset +04:00 rather than +03:00. It was between 31 October 2010 and 26 October 2014. I have reproduced your result on my Java 1.7.0_67 and verified that my Java installation “believes” that Moscow is at offset +04:00 and does not use summer time (DST), as was the case in the mentioned period.
Your Joda-Time seems to be new enough to know that Europe/Moscow is at +03:00 so correctly converts your MutableDateTime to a Date at 00:00 hours on the date in question. Only when you print this Date, Java uses its default time zone, still Europe/Moscow, but its own time zone data, and therefore incorrectly prints the time as 01:00 hours instead of 00:00.
Possible solutions include:
Upgrade to a newer Java version that has up-to-date time zone data.
Fix your current Java installation by upgrading only its time zone database. See Timezone Updater Tool in the second link below.
Setting the time to the start of the day
Edit: you added:
Here MutableDateTime time =new MutableDateTime(new Date().getTime());
To get a Date representing the start of today’s date using Joda-Time:
Date oldfashionedDateObject = LocalDate.now(DateTimeZone.getDefault()).toDate();
System.out.println(oldfashionedDateObject);
Output just now:
Mon May 31 00:00:00 MSK 2021
Original aside: As an aside, the simpler and safer way to set the time to the start of the day is:
mdt = mdt.toDateTime().withTimeAtStartOfDay().toMutableDateTime();
If you need to keep the same MutableDateTime object, instead do:
mdt.setMillis(mdt.toDateTime().withTimeAtStartOfDay().toInstant());
First of all I would be worried that your code may run in a time zone and on a day that in that time zone has a transition at 00:00 so that the first moment of the day is 01:00 or something else. In this case I b believe that your code would throw a surprising exception. Also I find setting individual fields low-level and prefer to set everything in one method call even if it requires further operations to determine the argument to pass to that method.
Links
Time Zone in Moscow, Russia (Moskva).
Joda-Time Updating the time zone data.
Timezone Updater Tool on Oracle’s web site.

Add 30 days in future from current epoch time Java

I am getting the current time in epoch. How can I add 1 month in future?
Date date = new Date();
int epoch = date.getTime();
Datatype for epoch - integer($int64)
To be precise: I want to add 30 days from current time.
I am using a tool that allows Groovy and Java code to be embedded. I used Date class because I can easily import java.text.DateFormat; and import java.text.SimpleDateFormat;. The tool that I have doesn't support Instant.
Since Java 8, use java.time for time usage
As epoch seconds, adding 30 days:
Instant.now().plus(30, ChronoUnit.DAYS).getEpochSecond()
As epoch milliseconds, adding 30 days:
Instant.now().plus(30, ChronoUnit.DAYS).toEpochMilli()
You don’t want to use Date, use date time API.
Instant.now().plus(30, ChronoUnit.DAYS)
Here's how I'd do it:
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 30);
long epoch = calendar.getTimeInMillis();
System.out.println(epoch);
Result:
1606785580218
This is milliseconds. You can divide by 1000 to get the version in seconds. NOTE: The seconds version will fit in an int whereas the milliseconds version requires a long. So it's OK to do this if you want the seconds version in an int:
long epoch = ...
int epochSeconds = (int)(epoch / 1000);
BTW, Unix time (Epoch time, POSIX time) is defined as the number of seconds since 1 January 1970 UTC. Some systems will return the value in milliseconds to provide more accuracy, but such a value is officially a fractional Epoch time multiplied by 1000.
So far all answers are wrong.
That's because what you want is impossible.
You're mixing entirely incompatible concepts. You're asking to add 1 entity that is devoid of timezone and political meaning ('epoch-millis', which explicitly means: No timezone info!) with a concept that cannot be nailed down as meaning anything particular unless you supply timezone and era.
You cannot add a month to an epoch milli. Not because java doesn't let you, or because it is hard to program. You can't do it for the same reason you cannot point at the corner in a circle. It's literally impossible, by definition.
'epoch millis' are a concept that is fundamentally about a moment in time. Things like 'when the sun flared up'. "When I clapped my hands together just now". This concept is best represented in java by an instance of java.time.Instant. It is also represented by a java.util.Date, which is funny, because this is nothing like a Date, and indeed, j.u.Date is an utterly stupid name, and the authors have belatedly realized this, which is why (almost) all of the methods it has are marked #Deprecated with a note describing that Date's very name is a total lie. j.u.Date does not represent dates.
A month, that's an entirely different can of worms. There's nothing solid about a month. It could be 28 days. 30 days. 31 days. 30 days and an extra second tossed in. It could be a month that doesn't even exist, or is only 11 days (when political areas switch timezones, you can get some really bizarre things happening).
So, how do you add 'a month' to any instant in time?
You can't. It's not possible. You'd have no idea what to add, because there is no way to figure out if it's 28 days, 29, 30, 31, let alone leap seconds and era weirdness (recently, samoa switched to the other side of the dateline, and as a consequence, their december was only 30 days. The famous 'october revolution' that introduced communism to russia happened in november, at least as far as the entire world (except the russians) were concerned, because only the russians were on the julian calendar where it was still october. As part of the whole 'communism will take over the world!' scheme one of the very first things they did was get on the gregorian same as the rest of the world, and as a consequence, this date does not exist, AT ALL, in russian history: 1 through 13 feb 1918. They just. . were skipped. One day you wake up in moscow, walk outside, ask somebody: Hey, what date is it (though probably in russian), and they say: Why, 31st of January, comrade. Next day you repeat that exercise and now it is February 19th. So, february 1918 in russia was 15 days long.
See why 'please add 1 month' is just not a thing that could possibly be done unless you tell me when and where? If you tell me 'in russia', then I still don't know if I add 15, or 31, or 30, or 29, or 28. If you tell me 'Februari, in 1918', well, in the rest of the world, februari was 28 days. Only in russia it was 15.
Now, adding 'a month' to some human-oriented date/time construct, such as 'well, right now, in amsterdam', ah, that works: "THIS month, the month it is right now, in amsterdam", that is a question that has an answer. But, 'epoch millis' is java-ese for: "An instant in time, devoid of any and all geographical information", and by missing geo info it is also impossible to know when that is relative to any timezone, and therefore, utterly impossible.
So, what CAN you do? Well, many things, but first you need to figure out what you want to do, and only then can somebody tell you how to do it:
I just want to add what feels like an average month, and end up with epoch-millis. Okay, then just add 2.629.800.000, which is a rough estimate (that's about 30.4375 days, which is roughly the average length of a month. I have no idea what possible purpose this would serve, but it's surely a better plan than adding 30 days, or 31 days, or 28 days.
I want to first translate this epoch-millis into a time as humans would say it (in turns of year, month, day, hour, minute, and second), and then just increment the month value by 1, and leave it at that, in e.g. a ZonedDateTime object. Okay, then first figure out which timezone you want, then turn your epoch-millis into a ZonedDateTime, and THEN we've arrived at a point where 'add a month to this please' even makes sense, so now we can do that: .plusMonths(1), voila.
Same as previous, but then convert that back to epoch millis. Okay, well, do the same thing, and call .toInstant() at the end, and toEpochMillis() on that.
Most other ideas boil down to: Your question makes no sense and cannot be answered.
NB: The above all use the java.time packages. All other options (java.util.Date and java.util.Calendar are broken and unwieldy; generally attempting to do this or any other date-related job in those APIs will either be impossible, will give wrong answers, or there is a way but it is hard to figure out how and the resulting code will be hard to maintain and hard to follow. Why would you voluntarily punch yourself in the face? Why would you voluntary use crap APIs? Don't do that.

Wrong results when adding milliseconds to java.util.date object

Current time is Sat Apr 04 15:02:00 AEST 2020.
In the following snippet, I create a Date object and add 86400000L milliseconds (1 day) to it:
Date date = new Date();
date.setTime(date.getTime() + 86400000L);
System.out.println(date);
The output is Sun Apr 05 14:02:00 AEST 2020. I don't understand why the result adds only 23 hours to my current time, instead of 24 hours.
Any help would be appreciated.
The code works just fine. The AEST on your output means that the date regards Australian Eastern Standard Time. Googling for AEST dst shows that on Sunday, April 5, 3:00 am 2020 the clock will "go back" 1 hour. Thus adding 24 hours just before the DST change, will only move the time 23 hours forward.
If you run that code tomorrow, you'll not have this "problem".
Do use java.time, the modern Java date and time API, for your date and time work.
ZonedDateTime currentTime = ZonedDateTime.now(ZoneId.of("Australia/Sydney"));
System.out.println(currentTime);
ZonedDateTime tomorrowSameTime = currentTime.plusDays(1);
System.out.println(tomorrowSameTime);
Output when running just now:
2020-04-04T16:00:30.579484+11:00[Australia/Sydney]
2020-04-05T16:00:30.579484+10:00[Australia/Sydney]
Please observe: we got the same time of day tomorrow, 16:00. Because summer time (daylight saving time) ends, the UTC offset for tomorrow is different, +10:00 instead of +11:00. And importantly, while I find + 86400000L pretty close to unreadable for adding a day, .plusDays(1) conveys the intention very clearly.
Please insert a different Eastern Australian time zone if required.
What went wrong in your code? cherouvim has explained this very nicely in the other answer, no need for me to repeat. Only allow me to add that the Date class is not only poorly designed — giving rise to your confusion — it is also long outdated. I recommend you don’t use it. And as cherouvim notes in a comment, programming with dates is hard. Don’t trust that you can yourself convert 1 day to 86 400 000 milliseconds. Leave all date and time calculations to proven library methods.
Link: Oracle tutorial: Date Time explaining how to use java.time.

Joda-time DateTime.withTimeAtStartOfDay() wrong result in date (2036,3,21) with Asia/Tehran time zone

In Joda-Time version 2.9.9 I want to remove time part of DateTime variable.
Only for time zone Asia/Tehran and some dates like (2036-03-21, 2037-03-21, ...) it returns 1:00:00 in time part of result.
I also checked the Joda-Time source code but I couldn't find any problem.
The code is:
DateTime dt = new DateTime(2036, 03, 21, 10, 0, DateTimeZone.forID(Asia/Tehran));
dt = dt.withTimeAtStartOfday();
Actual result:
2036-03-21T01:00:00.000+04:30
Expected result:
2036-03-21T00:00:00.000+04:30
Time is not zero. This only happens for zone Asia/Tehran.
My system config:
Java version: 1.7.0_72 - I have to use Java 7
Joda-Time: 2.9.9
I solved this problem by converting DateTime to LocalDate, but I want to know why this problem happens?
This is because Iran switches from Standard time to Daylight Savings time at 00:00 on the 21st or 22nd of March (whichever day contains the astronomical equinox).
In 2036 this happens on the 21st. In 2018 it happens to fall on the 22nd.
In short, the time jumps from 2036/03/20 24:00 to 2036/03/21 01:00. The hour from midnight to 1 AM does not exist on that specific day.
When writing code that deals with time, ALWAYS keep in mind that unexpected small offsets from expected results are almost surely due to administrative time changes. This is even more true for historical dates, where offsets could be any number of minutes and seconds, not just whole or half-hours.

JWT Issue date vs. Expiration date check is failing due to daylight savings time. What's a year-round permanent solution?

This test normally passes all year round when the there is no lost hour or gained hour to account for. But right now it's being a nuisance. We're validating that the expiration date, getExp(), minus 7 days, is within 10 milliseconds of the issue date, getIss(). Since the exp and iss values are generated right after each other 10 milliseconds is a perfectly acceptable delta to expect the values to be between.
But now they're understandably an hour off when it looks a week into the future when the Java date code calls setExp() after initializing a new JWT Claim.
Assertion / test code:
assertThat(claim.getExp()).isCloseTo(new DateTime(claim.getIss()).plusDays(7).toDate(), 10);
In english this reads: assert that the claim's expiration date is within 10 milliseconds of the claim's issue date + 7 days.
An immediate fix is to add an hour's worth of milliseconds to the allowed delta but I was curious to see if there was a better solution.
EDIT: I believe I found the issue. We initialize claim issue date and claim expiration date using ZonedDateTime.now():
Claim claim = new Claim();
claim.setIss(Date.from(ZonedDateTime.now(ZoneId.of("UTC")).toInstant()));
claim.setExp(Date.from(ZonedDateTime.now(ZoneId.of("UTC")).plusDays(7).toInstant()));
But when we validate with JODA time we assume our local rules for daylight savings time. This obviously causes an issue with GMT DST vs. American DST rules.
EDIT2: The fix involved 2 words and updating the unit test to respect UTC:
assertThat(claim.getExp()).isCloseTo(new DateTime(claim.getIss(), DateTimeZone.UTC).plusDays(7).toDate(), 10);
Your only two solutions are:
Change your reporting so that, instead of being based on local time, is uses UTC (ie, epoch unix time). This is the preferable solution from a tech point of view since it's a worldwide standard including most shared servers and data sources; no DST to worry about; and you can still have your reports calculate local time.
Alternatively, to find or add a reliable 'dateadd'-type function that take Daylight Savings into account.
I've always been surprised that all platforms don't have that functionality built-in. Granted, DST has a confusing, ever-evolving set of rules that are region-specific (by continent, country, and is some areas by "state" or even "county") ...but still.
Currently, in most of North America, DST:
Begins on the 2nd Sunday of March. ("Spring Forward" so clocks change from 01:59 to 03:00)
Ends on the 1st Sunday of November. ("Fall Back" from 01:59 to 01:00)
Windows API has functionality for calculating whether DST is in effect but really, no application should allow an application to display, for example in North America: "Sunday March 11, 2018 02:30:00" because that time doesn't exist.
...still, #1 is your preferable solution because accuracy and standardization are more straightforward.

Categories

Resources