Joda time week calculation reasoning - java

The code below demonstrates the problematic joda-time implementation of week calculation. This behavior is not a bug but a design decision Joda-Time uses the ISO standard Monday to Sunday week. (perhaps it should be a bug?)
Given a date I need to calculate the week number, this calculation must be i18n in nature. Meaning I must take into consideration the correct week numbering based on the regional settings of the user.
The demo code below shows wrong calculation by Joda-Time and correct calculation by the JDK, in the application we try to stick with Joda-Time being a superior solution for date manipulations. So, should I be mixing the two Time calculation libraries? I would obviously prefer not to, is this even a safe thing to do or would I come into corner cases (having experience with Date, Calendar I know for a fact that this is a painful issue for Java).
Bottom line: What is the recommended best-practice for the described requirement?
Problem demonstration code
Please see this online calendar displaying week numbers for correct week calculation example.
public class JodaTest {
static DateTimeFormatter formatter = DateTimeFormat.forPattern("ww yyyy");
static SimpleDateFormat jdkFormatter = new SimpleDateFormat("ww yyyy");
public static void main(String[] args) {
DateTime time = new DateTime(/*year*/2009, /*monthOfYear*/12, /*dayOfMonth*/6, /*hourOfDay*/23, /*minuteOfHour*/0, /*secondOfMinute*/0, /*millisOfSecond*/0);
StringBuilder buffer = new StringBuilder()
.append("Testing date ").append(time.toString()).append("\n")
.append("Joda-Time timezone is ").append(DateTimeZone.getDefault()).append(" yet joda wrongly thinks week is ").append(formatter.print(time)).append("\n")
.append("JDK timezone is ").append(TimeZone.getDefault().getID()).append(" yet jdk rightfully thinks week is ").append(jdkFormatter.format(time.toDate())).append(" (jdk got it right ?!?!)");
System.out.println(buffer.toString());
}
}
Output:
Testing date 2009-12-06T23:00:00.000+02:00
Joda-Time timezone is Asia/Jerusalem yet joda wrongly thinks week is 49 2009
JDK time zone is Asia/Jerusalem yet jdk rightfully thinks week is 50 2009 (jdk got it right ?!?!)

The best available solution is to write an implementation of DateTimeField that wraps up the logic to extract the value you need based on a locale. Internally, you'll probably still rely on the JDK data. The aim is to wrap all the JDK code in a single reusable class. You then use it like this:
int value = dateTime.get(new LocaleAwareWeekField("en_GB"));

Related

Getting the UTC timestamp in Java

An old Stack Overflow posting suggests that the way to get the UTC timestamp in Java is the following:
Instant.now() // Capture the current moment in UTC.
Unfortunately this does not work for me. I have a very simple program (reproduced below) which demonstrates different behavior.
On Windows: the time is the local time and it is labeled with the offset with GMT
On Linux: the time is again the local time, and it is labeled correctly for the local timezone
Question: How do we display the UTC timestamp in a Java program?
My sample source code is as follows:
import java.time.Instant;
import java.util.Date;
public class UTCTimeDisplayer {
public static void main(String[] args) {
System.out.println(System.getProperty("os.name"));
Date currentUtcTime = Date.from(Instant.now());
System.out.println("Current UTC time is " + currentUtcTime);
}
}
Windows Output:
C:\tmp>java UTCTimeDisplayer
Windows 10
Current UTC time is Fri Jan 22 14:28:59 GMT-06:00 2021
Linux Output:
/tmp> java UTCTimeDisplayer
Linux
Current UTC time is Fri Jan 22 14:31:10 MST 2021
Your code:
Date.from(Instant.now())
You are mixing the terrible legacy classes with their replacement, the modern java.time classes.
Don’t.
Never use Date. Certainly no need to mix with java.time.Instant.
To explain your particular example, understand that among the Date class’ many poor design choices is the anti-feature of its Date#toString method implicitly applying the JVM’s current default time zone while generating its text.
You ran your code on two different JVMs that had different current default time zones. So you got different outputs.
Sun, Oracle, and the JCP gave up on the legacy date-time classes. So should we all. I recommend you not spend time trying understand Date, Calendar, SimpleDateFormat, and such.
You asked:
Question: How do we display the UTC timestamp in a Java program?
Instant.now().toString()
See that code run live at IdeOne.com.
2021-01-22T21:50:18.887335Z
You said:
On Windows: …
On Linux: …
You’ll get the same consistent results from Instant.now().toString() across Windows, Linux, BSD, macOS, iOS, Android, AIX, and so on.
Here is a table I made to guide you in transitioning from the legacy classes.
The java.util.Date object is not a real date-time object like the modern date-time types; rather, it represents the number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT (or UTC). When you print an object of java.util.Date, its toString method returns the date-time in the JVM's timezone, calculated from this milliseconds value. If you need to print the date-time in a different timezone, you will need to set the timezone to SimpleDateFormat and obtain the formatted string from it.
I would suggest you simply use Instant.now() which you can convert to other java.time type.
The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.
For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7.
If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
However, if you still want to use java.util.Date, use SimpleDateFormat as mentioned above.
Demo:
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) {
Date currentUtcTime = Date.from(Instant.now());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
System.out.println("Current UTC time is " + sdf.format(currentUtcTime));
}
}
Output:
Current UTC time is 2021-01-22 21:53:07 UTC
suggests that the way to get the UTC timestamp in Java is the following:
Instant.now() // Capture the current moment in UTC.
This, and most answers in this thread, are misleading.
Instant represents an instant in time. It's 'solarflares' time: Absolutely not one iota about it represents anything that is invented by human brains, and UTC is a timezone: A human invention. The cosmos, the sun, astronomy - they have no idea what UTC is, and don't care - and that's what Instant is all about. Instants are devoid of such human concepts as 'hours' or 'days' or 'timezones'. It makes no sense to ask an instant what day it happened on. It cannot tell you; some event occurred: If I ask a russian from the 19th century when that happened, they'll likely give a completely different answer vs. if I ask someone living a mere 100 miles west, for example. Instant doesn't know which localization to apply and thus doesn't let you ask it this question - that's a good thing, objects should not expose methods to which any answer it gives is either gobbledygook or at least requires knowing about all sorts of surprising caveats.
Crucially, if you tell me '... in UTC', you surely can tell with exacting detail which month, which day, etcetera. And Instant does not do this, which is why it is misleading to say that a java.time.Instant represents a moment of time in UTC. It doesn't. It represents a moment in time (not in any particular timezone).
Yeah, internally Instant, just like Date, is just a light wrapper around what System.currentTimeMillis() returns: "millis since epoch", but the crucial thing to understand about it, is that 'UTC' is not part of what it means, and therefore, when you give an Instant instance to some other method (such as System.out.println, to a database via JDBC, etc), that method is under absolutely no obligation to assume that UTC is semantically relevant.
When you want to mix human notions of time keeping (years, days, months, hours, minutes, milliseconds, and, yeah, timezones) with the notion of a more or less absolute* time, the right answer is java.time.ZonedDateTime. Note that any representation of time in something that isn't java.time.* based is by definition broken, as it is in most programming languages - turns out time is a lot more complex than most takes on a library to represent it realize. The fact that java.time is in effect the 4th attempt at writing a time library should be ample indication that it's hard to get it right.
ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC);
THAT is what you want - that isn't just implementation-detail-wise what you want, but it is code that exactly describes what you mean: Right now, at the UTC time zone, stored in an object that semantically doesn't just store the right time but also stores, and tightly entangles into its very identity, that it is specifically in UTC and is not to be re-interpreted, moved to the local zone, or any other such shenanigans - at least, not unless you explicitly ask it to do so.
Date currentUtcTime = Date.from(Instant.now());
Note that Date is the old API and therefore necessarily broken. In this case, Date is a lying liar who lies - it doesn't represent a date, it represents an instant; it is badly named. (The second API is Calendar, also broken. For example, that is also a lying liar who lies: It doesn't represent a Calendar whatsoever. It represents some bizarre amalgamation of a zoned datetime and an instant and is fit to represent neither as a consequence). Any time you go to the Date API weirdness ensues, and something as simple as 'I just want the concept of the time, at some specific moment, in UTC' isn't possible in these APIs. You are now dependent on barely defined behaviour of all the various libraries up and down the chain - you're effectively stuck praying that they do the right thing, or delving into exotic settings to try to cajole these libraries into doing what you want.
TL;DR: Use java.time.
*) Note that ZonedDateTime is not absolute. For example, if you have the time January 20th, 2023, 8 in the morning, at Europe/Amsterdam, in the form of a ZonedDateTime object, then the amount of seconds that will pass between now and that moment sure seems like it does not change and will not change when e.g. amsterdam goes through an hour change due daylight savings. However, if the dutch government decrees that henceforth The Netherlands will no longer move the clocks at all and will stay in summer time forever (which is likely - EU directive is already in place, it's now just a matter of when), then at the moment the gavel lands, your appointment shifts by 1 hour exactly.
That hopefully provides crucial insight in the difference: Instant, representing events (hence why I like to call it 'solarflares time', to disentangle it from human time keeping concepts as much as possible), doesn't even understand the very concept of such a decision having an effect on things. ZonedDateTime on the other hand is inherently bound up in it - hence the Zone in ZonedDateTime.
If you want to store barber appointments and use Instant to do it, you WILL be an hour late or early sooner rather than later.
An Instant object and also a Date object by themselves
only contain a point in time, but no timezone information.
Furthermore, the toString() method of the Date class
implicitly chooses the timezone provided by the system environment,
which is not what you want.
Therefore you need to chose the timezone (in your case UTC) explicitly.
For example like this:
Instant instant = Instant.now();
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.UTC);
System.out.println("Current UTC time is " + offsetDateTime);
This will (independently from the operation system) print
Current UTC time is 2021-01-22T22:37:21.950354100Z
where the trailing Z denotes the zero timezone offset (i.e. UTC).
A simple method that could work!
My requirement was date time with milliseconds
2021-11-25 19:55:00.743
private String getUTCTimestamp() {
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);
return utc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
Instant.now() is essentially the period of time since the epoch, (midnight 1 January 1970 in UTC), but you are using a Date to present that instant. Date is a reflection of the instant with millisecond precision, but as explained in the documentation at https://docs.oracle.com/javase/8/docs/api/java/util/Date.html, presenting a date should be done using a Calendar, as the presentation of a Date depends on the host. Essentially Date wraps the instant but is displayed according to other factors.
The simplest approach now if you want to output the instant is to use OffsetDateTime so that you can elect to present the instant in your desired timezone - UTC in your case. Use either OffsetDateTime.now() or OffsetDateTime.ofInstant() but if you are using the instant within your application logic then just stick with Instant.
Sometimes your program has to work with older java versions, so here is an example for 1.5:
java.text.SimpleDateFormat tfGMT = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Calendar cUTC = java.util.Calendar.getInstance (java.util.TimeZone.getTimeZone ("GMT+0"));
tfGMT.setCalendar (cUTC);
java.util.Date d= new java.util.Date ();
String s= tfGMT.format (d);
System.out.printf ("now=%s [unix ts=%d.%03d]\n", s, d.getTime()/1000, d.getTime()%1000);
Mind you, the first three lines don't have to be repeat at every call, but keep in mind that SimpleDateFormat is not thread-safe. (Simple solution: create one for each thread.)
Example usage (it shows that setting TZ doesn't affect UTC-timestamp):
$ TZ=GMT+3 java5 now_utc; TZ=GMT-3 java5 now_utc
now=2021-01-24 12:56:14 [unix ts=1611492974.264]
now=2021-01-24 12:56:14 [unix ts=1611492974.726]

Incorrect data returned by Calendar API

In our business requirement we are supposed to find out the first day of the week.
Since the client is European so I used following code to get it.
But, issue is when I run this code I am getting 2 as the answer where as I am expecting 1.
public class Test{
public static void main(String[] args) {
TimeZone eu = TimeZone.getTimeZone("Europe/Berlin");
Locale de = Locale.forLanguageTag("de");
int firstDayOfWeek = Calendar.getInstance(eu, de).getFirstDayOfWeek();
System.out.println(firstDayOfWeek);
}
}
Can some one help me understand this behavior.
Look at the docs:
Gets what the first day of the week is; e.g., SUNDAY in the U.S., MONDAY in France.
and here:
public static final int MONDAY 2
This shows that the return value depends on the locale.
If we print this:
System.out.println(firstDayOfWeek == Calendar.MONDAY);
We'll see true. This means that the first day of week is indeed Monday. It's just that you thought Monday is encoded by 1. In fact, Sunday is encoded as 1.
Locale de = Locale.forLanguageTag("de");
WeekFields wf = WeekFields.of(de);
DayOfWeek firstDayOfWeek = wf.getFirstDayOfWeek();
System.out.println(firstDayOfWeek);
This prints:
MONDAY
Don’t use the confusing old Calendar class. You got 2 as the first day of the week. But weren’t you supposed to get 1? Or 0? How are days of the week numbered? Forget about numbers, use the DayOfWeek enum from java.time, the modern Java date and time API.
For prettier output use DayOfWeek.getDisplayName or a DateTimeFormatter. If you do need a number, use DayOfWeek.getValue:
System.out.println(firstDayOfWeek.getDisplayName(TextStyle.FULL, de));
System.out.println(firstDayOfWeek.getValue());
Output is:
Montag
1
PS Also don’t use TimeZone and the other long outdated date and time classes. java.time is so much nicer to work with. This fact shows nicely in this example, but its typical, certainly no coincidence.
Link: Oracle tutorial: Date Time explaining how to use java.time.
According to the javadoc, MONDAY's valus is 2.

Date.getTime() in Java returning different time in different server

I need to calculate a value that is based on dates. So I'm parsing the date first using date format class and then i'm using getTime() to get milliseconds. With those milliseconds i'll calculate some value. But getTime() is returning different values in different servers. We develop here in India, where i'm getting correct value but in US server i'm getting different value.
Scenario:
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
String now = "11/03/2018";
Date UsualDateformat = sdf.parse(now);
System.out.println(UsualDateformat.getTime());
}
}
Above is a sample code, but my actual code is a rule in a drl file (drools).
This program returns
"1541183400000" which i convert to date is "Sat 3 November 2018 00:00:00".,
but in US server im getting "1541217600000" equal to date "Sat 3 November 2018 09:30:00".
So when i use this value i'm getting marginal decimal point formatting issue.
How to resolve this issue?
Thanks in advance!
You need to set the time zone on SimpleDateFormat to be consistent across servers in different regions. For example:
public class Test {
public static void main(String[] args) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
....
}
}
You get these different values, because the time difference between your server in the US and the one in India is 9h 30.
This has nothing to do with floating points, but with time zones.
One way to solve this problem is to work always within the same time zone (e.g. India)
This code snippet might work for you,
public static String getGmtTime(String timezone) {
return ZonedDateTime
.now()
.withZoneSameInstant(ZoneId.of(timezone))
.toLocalDateTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
pass timezone as America/Los_Angeles to get time with proper timezone,
Timezones can be found here
java.time
I recommend you use java.time, also known as JSR-310, for this. The classes you use, Date and SimpleDateFormat, are long outdated, and SimpleDateFormat in particular has a reputation for producing surprising results, which one may say also happened in your case. The modern API is generally so much nicer to work with.
As I think you have suspected already, your issue comes from the fact that your servers are running different time zones, and the conversion of a date to millis since the epoch is a time zone dependent operation since the epoch is always the same point in time (Jan 1 1970 at midnight in UTC). As mweiss I am using UTC for the conversion to make sure it gives the same result no matter the time zone of the server:
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String now = "11/03/2018";
LocalDate date = LocalDate.parse(now, dtf);
long millisInUtc = date.atStartOfDay(ZoneOffset.UTC)
.toInstant()
.toEpochMilli();
System.out.println(millisInUtc);
As my code stands, it prints
1541203200000
This is between the values you got in India and US because UTC is between those two time zones. If you consider it more correct to use for example Asia/Kolkata time, simply substitute ZoneId.of("Asia/Kolkata") instead of ZoneOffset.UTC in the code, and you should get the same output as you already got when you ran your code on your server in India (please remember to rename the variable too).
My code is a bit longer than yours. I consider this an advantage in this case. Yes, indeed. The code using java.time is making explicit that we are using the time at the start of the day (0:00 midnight) and that we are using a time zone or offset for the conversion. This forces you as the coder to think about these issues, and you will be very unlikely to write code that produces results that differ unexpectedly across time zones, that is, your issue would never arise. At the same time it makes explicit to the reader that the operation depends on time zone, and that you have made a conscious choice of which zone to use. These advantageous are well worth a few more code lines.

Joda api not return proper islamic date

I am converting Gregorian dates to Islamic dates. I am setting its leap year pattern to Indian leap year but it is not working.
I make for loop and Gregorian date which takes current month and count is days and convert it to Islamic date. What i want
Here is my code
for(int i=0;i<maxDay;i++)
{
eng.add(String.valueOf(i+1));
DateTime dtISO=new DateTime(currentY,currentMonth+1,i+1,0,0);
DateTimeZone asia= DateTimeZone.forID("Asia/Riyadh");
DateTime dtIslamic = dtISO.withChronology(
IslamicChronology.getInstance(
asia,
IslamicChronology.LEAP_YEAR_INDIAN));
String islamicDateArr="";
split=dtIslamic.toString().split("T");
split=split[0].split("-");
if(i==0 || Integer.parseInt(split[2])==1)
{
isl.add(String.valueOf(split[2]+" "+islamicMonths[Integer.parseInt(split[1])-1]));
continue;
}
isl.add(String.valueOf(split[2]));
}
Your code seems to be correct.
Since you told me to have tried every of the four leap year patterns of Joda-Time without success I get the feeling there might be a bug or just a missing feature because among all supported leap year patterns there should be a pair of patterns which are different by one day (and you observe one day difference).
Other people have already submitted bug issues. See here:
issue 163
issue 107
As you can see it will be hard for you to convince the project leader to solve your problem for you. Maybe he is right when saying that Joda-Time has not a bug, but is just not complete.
Keep in mind that according to R.H. van Gent the calculated islamic calendar algorithm knows at least 8 instead of 4 variants because there are 4 intercalary schemes and (for each scheme) two variations depending on the precise start of the islamic epoch (Thursday versus Friday epoch). So Joda-Time is just not supporting all variants.
What are the alternatives to Joda-Time?
a) Java-8 and its backport Threeten-BP (for Java-6+7) support the table-driven Umalqura-calendar of Saudi-Arabia (sighting-based). I am not sure if this solves your problem however (if not then you might supply a hand-written file containing the table data relevant for you - a lot of work). Note that both libraries don't support algorithm-based islamic calendars.
b) Some people have written their own home-grown workarounds. I have found this hijri converter via Google. No idea if this works for you.
c) IBM offers a Hijri calendar in its ICU-project. It offers different leap year patterns than Joda-Time. Maybe it helps.
Side note: As you can see the current Java-support for Hijri calendars is not really satisfying. That is why I decided to set up a new implementation in my own library Time4J. It is scheduled for maybe 2-3 months later in autumn 2015.

Why has java.util.Date been deprecated? [duplicate]

When you look at the javadoc of the java.util.Date class, most of the methods are deprecated. Why was this done?
Well, for two related reasons. It was a very poor implementation of the concept of Dates and Times and it was replaced by the Calendar class.
The Calendar class, although an improvement, leaves a lot to be desired as well, so for serious Date/Time work, everyone recommends Joda-Time. Java 8 brings the new java.time.* package, inspired by Joda-Time, defined by JSR-310, and intended to supplant the old Date/Calendar classes.
Edit: In response to the specific question of why the implementation is poor, there are many reasons. The JavaDoc sums it up as follows:
Unfortunately, the API for these functions was not amenable to internationalization.
In addition to this general deficiency (which covers issues like the lack of a Time Zone component as well as the date formatting which is better handled in DateFormat and the inability to have a non-Gregorian calendar representation), there are specific issues which really hurt the Date class, including the fact that year is presented in an offset of 1900 from Common Era year.
Calendar has its own problems, but even as early as JDK 1.1 it was obvious that java.util.Date was not going to cut it. Even though Calendar is arguable the worst JDK API, it has taken until version 7 to attempt to address it.
Date is mutable
Date doesn't have support for time zones
The latter led to it being replaced by Calendar. And the former, combined with the ease-of-use, lead to both being replaced by Joda-Time / JSR-310 (java.time.* package)
They're deprecated because Date was written as fast as possible back in the day when they wanted to rush the JDK out the door.
It turns out the Dates and Calendars are Hard. So, they created the Calendar class, which much more thought, in order to handle the Hard Parts of working with calendars.
They deprecated the Date methods and delegated to Calendar because they didn't want to change the behavior of the existing Date methods, and possibly break existing applications.
Here's a good answer straight from Oracle: http://www.oracle.com/technetwork/articles/java/jf14-date-time-2125367.html
A long-standing bugbear of Java developers has been the inadequate support for the date and time use cases of ordinary developers.
For example, the existing classes (such as java.util.Date and SimpleDateFormatter) aren’t thread-safe, leading to potential concurrency issues for users—not something the average developer would expect to deal with when writing date-handling code.
Some of the date and time classes also exhibit quite poor API design. For example, years in java.util.Date start at 1900, months start at 1, and days start at 0—not very intuitive.
... java.util.Date represents an instant on the timeline—a wrapper around the number of milli-seconds since the UNIX epoch—but if you call toString(), the result suggests that it has a time zone, causing confusion among developers.
I don't know the official reason why it has been deprecated, but as far as I can tell GregorianCalendarand Joda-Time support operations on dates, meaning that you can add, for instance, a day to a date and have its month and year updated accordingly.
For instance, say you want to compute the day after the current date and today is May 31st; with java.util.Date, you just have getDays() +1, which returns 32, and you have to handle the knowledge that the current month doesn't have 32 days by yourself; with GregorianCalendaror Joda.time, adding a day to May 31st results in an object representing June 1st, hiding the complexity from your sight.

Categories