Why Calendar.SEPTEMBER is not working? [closed] - java

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I want to see a month which contains whole days.
private void createRandomData(InMemoryCursor cursor) {
List<Object[]> data = new ArrayList<>();
Calendar today = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
today.set(Calendar.HOUR_OF_DAY,0);
today.set(Calendar.MINUTE, 0);
today.set(Calendar.SECOND, 0);
today.set(Calendar.MILLISECOND, 0);
mStart = (Calendar) today.clone();
mStart.add(Calendar.SEPTEMBER, -5);
while (mStart.compareTo(today) <= 0) {
data.add(createItem(mStart.getTimeInMillis()));
mStart.add(Calendar.SEPTEMBER, 1);
}
cursor.addAll(data);
}
When I write Calendar.SEPTEMBER(or other months), I see red line on Calendar.SEPTEMBER which contains:
Must be one of: Calendar.ERA, Calendar.YEAR, Calendar.MONTH, Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND, Calendar.ZONE_OFFSET, Calendar.DST_OFFSET less... (Ctrl+F1)
This inspection looks at Android API calls that have been annotated with various support annotations (such as RequiresPermission or UiThread) and flags any calls that are not using the API correctly as specified by the annotations. Examples of errors flagged by this inspection:
Passing the wrong type of resource integer (such as R.string) to an API that expects a different type (such as R.dimen).
Forgetting to invoke the overridden method (via super) in methods that require it
Calling a method that requires a permission without having declared that permission in the manifest
Passing a resource color reference to a method which expects an RGB integer value.
...and many more. For more information, see the documentation at developer.android.com/tools/debugging/annotations.html
When I run it despite the red line, It shows complicated dates like:
see
I use this library from GitHub:https://github.com/jruesga/timeline-chart-view
Is problem related to library? or It is about Java calendar?

As explained in #Michael's answer, you can't use Calendar.SEPTEMBER in the add method.
If you want to add or subtract a specified number of months, just use Calendar.MONTH. If you want to add/subtract days, you use Calendar.DAY_OF_MONTH and so on.
The Calendar API might be confusing sometimes (most times, IMO), and has lots of problems and design issues.
In Android, there's a better alternative: you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. To make it work, you'll also need the ThreeTenABP (more on how to use it here).
As you're getting a Calendar in the default timezone, a good candidate for replacement is a org.threeten.bp.ZonedDateTime (it represents a date and time in a specific timezone). First I use the now() method (that takes the current date/time at the JVM default timezone). Then I use a org.threeten.bp.LocalTime to set the time to midnight.
I also use the minusMonths method to get a date 5 months before the current date, and inside the loop I use the toInstant() method, to get the millis value, and the plusMonths method to get the next month:
// get today at default timezone, at midnight
ZonedDateTime today = ZonedDateTime.now().with(LocalTime.MIDNIGHT);
// 5 months ago
ZonedDateTime start = today.minusMonths(5);
while (start.compareTo(today) <= 0) {
data.add(createItem(start.toInstant().toEpochMilli()));
start = start.plusMonths(1);
}
If you want to add/subtract minutes instead of months, for example, you can use the methods minusMinutes and plusMinutes. There are other methods for another units as well (such as hours, days, and so on), check the javadoc to see all the options.
The problem of using the default timezone is that it can be changed without notice, even at runtime, so it's better to always make it explicit which one you're using.
With Calendar, you can use TimeZone.getTimeZone(zoneName):
Calendar todayCal = Calendar.getInstance(TimeZone.getTimeZone("Europe/London"), Locale.getDefault());
And with ThreeTen Backport, you can use ZoneId.of(zoneName):
ZonedDateTime today = ZonedDateTime.now(ZoneId.of("Europe/London")).with(LocalTime.MIDNIGHT);
In the example above, I used Europe/London, but you can change it to any timezone you want. The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds() or TimeZone.getAvailableIDs().

You are using an incompatible option. The first parameter of Calendar.add() is a Unit of Time (Day, Week, Hour etc) as defined by the possible options outlined in the error. Calendar.SEPTEMBER is not a unit of time, it is a convenience constant representing the MONTH of September that is typically used in the set() method instead.
Assuming you're iterating through months, you'll need Calendar.MONTH instead.

Related

SimpleDateFormat error while parsing a date yyyyMMdd format java [duplicate]

In java.util.Calendar, January is defined as month 0, not month 1. Is there any specific reason to that ?
I have seen many people getting confused about that...
It's just part of the horrendous mess which is the Java date/time API. Listing what's wrong with it would take a very long time (and I'm sure I don't know half of the problems). Admittedly working with dates and times is tricky, but aaargh anyway.
Do yourself a favour and use Joda Time instead, or possibly JSR-310.
EDIT: As for the reasons why - as noted in other answers, it could well be due to old C APIs, or just a general feeling of starting everything from 0... except that days start with 1, of course. I doubt whether anyone outside the original implementation team could really state reasons - but again, I'd urge readers not to worry so much about why bad decisions were taken, as to look at the whole gamut of nastiness in java.util.Calendar and find something better.
One point which is in favour of using 0-based indexes is that it makes things like "arrays of names" easier:
// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];
Of course, this fails as soon as you get a calendar with 13 months... but at least the size specified is the number of months you expect.
This isn't a good reason, but it's a reason...
EDIT: As a comment sort of requests some ideas about what I think is wrong with Date/Calendar:
Surprising bases (1900 as the year base in Date, admittedly for deprecated constructors; 0 as the month base in both)
Mutability - using immutable types makes it much simpler to work with what are really effectively values
An insufficient set of types: it's nice to have Date and Calendar as different things,
but the separation of "local" vs "zoned" values is missing, as is date/time vs date vs time
An API which leads to ugly code with magic constants, instead of clearly named methods
An API which is very hard to reason about - all the business about when things are recomputed etc
The use of parameterless constructors to default to "now", which leads to hard-to-test code
The Date.toString() implementation which always uses the system local time zone (that's confused many Stack Overflow users before now)
Because doing math with months is much easier.
1 month after December is January, but to figure this out normally you would have to take the month number and do math
12 + 1 = 13 // What month is 13?
I know! I can fix this quickly by using a modulus of 12.
(12 + 1) % 12 = 1
This works just fine for 11 months until November...
(11 + 1) % 12 = 0 // What month is 0?
You can make all of this work again by subtracting 1 before you add the month, then do your modulus and finally add 1 back again... aka work around an underlying problem.
((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!
Now let's think about the problem with months 0 - 11.
(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January
All of the months work the same and a work around isn't necessary.
C based languages copy C to some degree. The tm structure (defined in time.h) has an integer field tm_mon with the (commented) range of 0-11.
C based languages start arrays at index 0. So this was convenient for outputting a string in an array of month names, with tm_mon as the index.
There has been a lot of answers to this, but I will give my view on the subject anyway.
The reason behind this odd behavior, as stated previously, comes from the POSIX C time.h where the months were stored in an int with the range 0-11.
To explain why, look at it like this; years and days are considered numbers in spoken language, but months have their own names. So because January is the first month it will be stored as offset 0, the first array element. monthname[JANUARY] would be "January". The first month in the year is the first month array element.
The day numbers on the other hand, since they do not have names, storing them in an int as 0-30 would be confusing, add a lot of day+1 instructions for outputting and, of course, be prone to alot of bugs.
That being said, the inconsistency is confusing, especially in javascript (which also has inherited this "feature"), a scripting language where this should be abstracted far away from the langague.
TL;DR: Because months have names and days of the month do not.
In Java 8, there is a new Date/Time API JSR 310 that is more sane. The spec lead is the same as the primary author of JodaTime and they share many similar concepts and patterns.
I'd say laziness. Arrays start at 0 (everyone knows that); the months of the year are an array, which leads me to believe that some engineer at Sun just didn't bother to put this one little nicety into the Java code.
Probably because C's "struct tm" does the same.
Because programmers are obsessed with 0-based indexes. OK, it's a bit more complicated than that: it makes more sense when you're working with lower-level logic to use 0-based indexing. But by and large, I'll still stick with my first sentence.
java.time.Month
Java provides you another way to use 1 based indexes for months. Use the java.time.Month enum. One object is predefined for each of the twelve months. They have numbers assigned to each 1-12 for January-December; call getValue for the number.
Make use of Month.JULY (Gives you 7)
instead of Calendar.JULY (Gives you 6).
(import java.time.*;)
Personally, I took the strangeness of the Java calendar API as an indication that I needed to divorce myself from the Gregorian-centric mindset and try to program more agnostically in that respect. Specifically, I learned once again to avoid hardcoded constants for things like months.
Which of the following is more likely to be correct?
if (date.getMonth() == 3) out.print("March");
if (date.getMonth() == Calendar.MARCH) out.print("March");
This illustrates one thing that irks me a little about Joda Time - it may encourage programmers to think in terms of hardcoded constants. (Only a little, though. It's not as if Joda is forcing programmers to program badly.)
For me, nobody explains it better than mindpro.com:
Gotchas
java.util.GregorianCalendar has far fewer bugs and gotchas than the
old java.util.Date class but it is still no picnic.
Had there been programmers when Daylight Saving Time was first
proposed, they would have vetoed it as insane and intractable. With
daylight saving, there is a fundamental ambiguity. In the fall when
you set your clocks back one hour at 2 AM there are two different
instants in time both called 1:30 AM local time. You can tell them
apart only if you record whether you intended daylight saving or
standard time with the reading.
Unfortunately, there is no way to tell GregorianCalendar which you
intended. You must resort to telling it the local time with the dummy
UTC TimeZone to avoid the ambiguity. Programmers usually close their
eyes to this problem and just hope nobody does anything during this
hour.
Millennium bug. The bugs are still not out of the Calendar classes.
Even in JDK (Java Development Kit) 1.3 there is a 2001 bug. Consider
the following code:
GregorianCalendar gc = new GregorianCalendar();
gc.setLenient( false );
/* Bug only manifests if lenient set false */
gc.set( 2001, 1, 1, 1, 0, 0 );
int year = gc.get ( Calendar.YEAR );
/* throws exception */
The bug disappears at 7AM on 2001/01/01 for MST.
GregorianCalendar is controlled by a giant of pile of untyped int
magic constants. This technique totally destroys any hope of
compile-time error checking. For example to get the month you use
GregorianCalendar. get(Calendar.MONTH));
GregorianCalendar has the raw
GregorianCalendar.get(Calendar.ZONE_OFFSET) and the daylight savings
GregorianCalendar. get( Calendar. DST_OFFSET), but no way to get the
actual time zone offset being used. You must get these two separately
and add them together.
GregorianCalendar.set( year, month, day, hour, minute) does not set
the seconds to 0.
DateFormat and GregorianCalendar do not mesh properly. You must
specify the Calendar twice, once indirectly as a Date.
If the user has not configured his time zone correctly it will default
quietly to either PST or GMT.
In GregorianCalendar, Months are numbered starting at January=0,
rather than 1 as everyone else on the planet does. Yet days start at 1
as do days of the week with Sunday=1, Monday=2,… Saturday=7. Yet
DateFormat. parse behaves in the traditional way with January=1.
The true reason why
You would think that when we deprecated most of Date and added the new
Calendar class, we would have fixed Date's biggest annoyance: the fact
that January is month 0. We certainly should have, but unfortunately
we didn't. We were afraid that programmers would be confused if Date
used zero-based months and Calendar used one-based months. And a few
programmers probably would have been. But in hindsight, the fact that
Calendar is still zero-based has caused an enormous amount of
confusion, and it was probably the biggest single mistake in the Java
international API's.
Quoted from International Calendars in Java by Laura Werner, link at the bottom.
The better alternative: java.time
This may just be repeating what others have said, throw the old and poorly designed Calendar class overboard and use java.time, the modern Java date and time API. There months are consistently sanely numbered from 1 for January through 12 for December.
If you are getting a Calendar from a legacy API not yet upgraded to java.time, the first thing to do is to convert to a modern ZonedDateTime. Depending on your needs you may do further conversions from there. In most of the world the Calendar object you get will virtually always be an instance of the GregorianCalendar subclass (since the Calendar class itself is abstract). To demonstreate:
Calendar oldfashionedCalendarObject = Calendar.getInstance();
ZonedDateTime zdt
= ((GregorianCalendar) oldfashionedCalendarObject).toZonedDateTime();
System.out.println(zdt);
System.out.format("Month is %d or %s%n", zdt.getMonthValue(), zdt.getMonth());
Output when I ran just now in my time zone:
2021-03-17T23:18:47.761+01:00[Europe/Copenhagen]
Month is 3 or MARCH
Links
International Calendars in Java by Laura Werner
Oracle tutorial: Date Time explaining how to use java.time.
tl;dr
Month.FEBRUARY.getValue() // February → 2.
2
Details
The Answer by Jon Skeet is correct.
Now we have a modern replacement for those troublesome old legacy date-time classes: the java.time classes.
java.time.Month
Among those classes is the Month enum. An enum carries one or more predefined objects, objects that are automatically instantiated when the class loads. On Month we have a dozen such objects, each given a name: JANUARY, FEBRUARY, MARCH, and so on. Each of those is a static final public class constant. You can use and pass these objects anywhere in your code. Example: someMethod( Month.AUGUST )
Fortunately, they have sane numbering, 1-12 where 1 is January and 12 is December.
Get a Month object for a particular month number (1-12).
Month month = Month.of( 2 ); // 2 → February.
Going the other direction, ask a Month object for its month number.
int monthNumber = Month.FEBRUARY.getValue(); // February → 2.
Many other handy methods on this class, such as knowing the number of days in each month. The class can even generate a localized name of the month.
You can get the localized name of the month, in various lengths or abbreviations.
String output =
Month.FEBRUARY.getDisplayName(
TextStyle.FULL ,
Locale.CANADA_FRENCH
);
février
Also, you should pass objects of this enum around your code base rather than mere integer numbers. Doing so provides type-safety, ensures a valid range of values, and makes your code more self-documenting. See Oracle Tutorial if unfamiliar with the surprisingly powerful enum facility in Java.
You also may find useful the Year and YearMonth classes.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
Set the month to Calendar.MARCH, or compare to see if it == Calendar.JUNE, for example.
The Date and Calendar classes date back to the very early days of Java, when folks were still figuring things out, and they are widely regarded as not very well designed.
If Calendar were created today with the same design, rather than ints for Calendar.JUNE, etc., they'd use enums.
It isn't exactly defined as zero per se, it's defined as Calendar.January. It is the problem of using ints as constants instead of enums. Calendar.January == 0.
Because language writing is harder than it looks, and handling time in particular is a lot harder than most people think. For a small part of the problem (in reality, not Java), see the YouTube video "The Problem with Time & Timezones - Computerphile" at https://www.youtube.com/watch?v=-5wpm-gesOY. Don't be surprised if your head falls off from laughing in confusion.
In addition to DannySmurf's answer of laziness, I'll add that it's to encourage you to use the constants, such as Calendar.JANUARY.
Because everything starts with 0. This is a basic fact of programming in Java. If one thing were to deviate from that, then that would lead to a whole slue of confusion. Let's not argue the formation of them and code with them.

Adding 1 hour to 13 digit Timestamp

I tried the code below.
Timestamp timestampDate = scheduled.getInterviewDateAndTime(); //from DB
Map<String, Object> map = new HashMap();
map.put("eventTitle", "interview with");
map.put("startDateTime", timestampDate);
System.out.println("startDateTime : " + timestampDate);
long addTime = 1*60*60*1000;
timestampDate.setTime(timestampDate.getTime() + TimeUnit.HOURS.toMillis(addTime));
map.put("endDateTime", timestampDate);
System.out.println("endDateTime : " + timestampDate);
Is this the correct way or are there any good alternative approaches?
output is:
startDateTime : 2017-11-07 09:08:00.0
endDateTime : 2428-07-15 09:08:00.0
how to get correct output?
There are several problems here:
java.sql.Timestamp (I assume that's what it is) is a mutable class, so setting a time on it changes the timestamp state. Even if it is not apparent with your println statements, debugging the map afterwards will reveal it in a heartbeat.
You logic for computing hours is wrong (you're multiplying two times there)
First time when making addTime variable.
Second time when using TimeUnit.toMillis()
There are (of course) several ways of fixing this:
The way I like more (it, however, requires Java 8 or ThreeTen library):
Timestamp start = obtain();
Timestamp end = Timestamp.from(start.toInstant().plus(1, ChronoUnit.HOURS));
It utilizes ability to convert a Timestamp to a java.time.Instant object (or equivalent version from ThreeTen), and then a factory constructor that will take an Instant and make a Timestamp out of it (this is Java 8 version, ThreeTen will have similar factory in other class not on Timestamp). It also utilizes a much cleaner time computation logic added in java.time compared to old datetime/calendar classes from JDK.
The second variant, which is about the same, doesn't use all the fancy stuff, but as a result is also much less readable (to me):
Timestamp start = obtain();
Timestamp end = new Timestamp(start.getTime() + TimeUnit.HOURS.toMillis(1));
As you can see, second variant is almost what you have, but without unnecessary computation.
And please don't compute these values manually; we all know that hour is supposed to be 60 minutes long and a minute is 60 second, etc., but reading this all over the place blurs eyes very quickly. You yourself has seen it by computing it manually first and then still using the TimeUnit one. And it's even worse when you need to add a day, because millis will not let you to add exactly one day without pulling a lot of information about day length at some specific point in time, considering daylight savings times and historical timezone changes. Not every minute is 60 seconds as well, there are compensative measures for those too, you know.
My recommendation for you is that you stop using the long outdated Timestamp class. Either completely if you can, or at least you minimize your use of it. I will show you code for both options. The modern Java date and time API known as java.time or JSR-310 is so much nicer to work with. And even more so when it comes to time arithmetic like adding an hour to a date-time.
java.time
Change getInterviewDateAndTime() to return an Instant. Instant is the class from java.time that naturally replaces the old Timestamp class. Also change the receiver of your Map to accept a map with Instant objects in it. Modern versions of JDBC, JPA, etc., happily retrieve Instant objects from your database and store Instants back into it.
Instant instantStart = scheduled.getInterviewDateAndTime(); //from DB
Map<String, Object> map = new HashMap<>();
map.put("eventTitle", "interview with");
map.put("startDateTime", instantStart);
System.out.println("startDateTime : " + instantStart);
Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
map.put("endDateTime", instantEnd);
System.out.println("endDateTime : " + instantEnd);
Things to note: The code much more naturally and straightforward expresses the fact that one hour is added. No need for multiplications, no need for the reader to check that you multiplied the right constants, or that your multiplication didn’t overflow; it’s all taken care of. The Instant class is immutable, so there’s no risk of accidentally changing the Instant object that you have alrady added to the map.
In my example the above code printed:
startDateTime : 2017-11-29T09:15:00Z
endDateTime : 2017-11-29T10:15:00Z
Times are in UTC.
EDIT: As Basil Bourque helpfully pointed out in a comment, adding an hour can also be done this way:
Instant instantEnd = instantStart.plus(Duration.ofHours(1));
The result is the same.
Use with legacy APIs
Assume you cannot change the return type of getInterviewDateAndTime() and/or the receiver of your map absolutely needs Timestamp objects in it. The standard way to go about such a restriction is you still use the modern API in your own code. As soon as you receive a Timestamp, you convert it to Instant. And when you need to pass a Timestamp to your legacy code, you convert your Instant only in the last moment.
Timestamp timestampStart = scheduled.getInterviewDateAndTime(); //from DB
Instant instantStart = timestampStart.toInstant();
Map<String, Object> map = new HashMap<>();
map.put("eventTitle", "interview with");
map.put("startDateTime", timestampStart);
System.out.println("startDateTime : " + timestampStart);
Instant instantEnd = instantStart.plus(1, ChronoUnit.HOURS);
Timestamp timestampEnd = Timestamp.from(instantEnd);
map.put("endDateTime", timestampEnd);
System.out.println("endDateTime : " + timestampEnd);
You still have most of the advantages of the modern API mentioned above, but a couple of extra lines for the conversions. This code printed
startDateTime : 2017-11-29 10:15:00.0
endDateTime : 2017-11-29 11:15:00.0
The times are the same as above, but in my local time zone since Timestamp.toString() renders them this way (which confuses many).
Question: Can I use the modern API with my Java version?
If using at least Java 6, you can.
In Java 8 and later the new API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310). Conversion between Timestamp and Instant is a little different and goes through a class named DateTimeUtils, but it’s not more complicated. The rest is the same.
On Android, use the Android edition of ThreeTen Backport. It’s called ThreeTenABP, and I think that there’s a wonderful explanation in this question: How to use ThreeTenABP in Android Project.

Problems when moving from SimpleDateFormat to DateTimeFormatter

I have been successfully using SimpleDateFormat for the last couple of years. I built a bunch of time utility classes using it.
As I ran into problems with SimpleDateFormat (SDF) not being thread safe, I spent the last couple of days refactoring these utility classes to internally use DateTimeFormatter (DTF) now. Since both classes' time patterns are almost identical, this transition seemed a good idea at the time.
I now have problems obtaining EpochMillis (milliseconds since 1970-01-01T00:00:00Z): While SDF would e.g. interpret 10:30 parsed using HH:mm as 1970-01-01T10:30:00Z, DTF does not do the same. DTF can use 10:30 to parse a LocalTime, but not a ZonedDateTime which is needed to obtain EpochMillis.
I understand that the objects of java.time follow a different philosophy; Date, Time, and Zoned objects are kept separately. However, in order for my utility class to interpret all strings as it did before, I need to be able to define the default parsing for all missing objects dynamically. I tried to use
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.parseDefaulting(ChronoField.YEAR, 1970);
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1);
builder.parseDefaulting(ChronoField.DAY_OF_MONTH, 1);
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
builder.append(DateTimeFormatter.ofPattern(pattern));
but this does not work for all patterns. It seems to only allow defaults for parameters that are not defined in pattern. Is there a way to test which ChronoFields are defined in pattern to then selectively add defaults?
Alternatively, I tried
TemporalAccessor temporal = formatter.parseBest(time,
ZonedDateTime::from,
LocalDateTime::from,
LocalDate::from,
LocalTime::from,
YearMonth::from,
Year::from,
Month::from);
if ( temporal instanceof ZonedDateTime )
return (ZonedDateTime)temporal;
if ( temporal instanceof LocalDateTime )
return ((LocalDateTime)temporal).atZone(formatter.getZone());
if ( temporal instanceof LocalDate )
return ((LocalDate)temporal).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof LocalTime )
return ((LocalTime)temporal).atDate(LocalDate.of(1970, 1, 1)).atZone(formatter.getZone());
if ( temporal instanceof YearMonth )
return ((YearMonth)temporal).atDay(1).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof Year )
return ((Year)temporal).atMonth(1).atDay(1).atStartOfDay().atZone(formatter.getZone());
if ( temporal instanceof Month )
return Year.of(1970).atMonth((Month)temporal).atDay(1).atStartOfDay().atZone(formatter.getZone());
which does not cover all cases either.
What is the best strategy to enable dynamic date / time / date-time / zone-date-time parsing?
Java-8-solution:
Change the order of your parsing instructions inside the builder such that the defaulting instructions all happen AFTER the pattern instruction.
For example using this static code (well, your approach will use an instance-based combination of different patterns, not performant at all):
private static final DateTimeFormatter FLEXIBLE_FORMATTER;
static {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("MM/dd");
builder.parseDefaulting(ChronoField.YEAR_OF_ERA, 1970);
builder.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1);
builder.parseDefaulting(ChronoField.DAY_OF_MONTH, 1);
builder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0);
builder.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0);
builder.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0);
FLEXIBLE_FORMATTER = builder.toFormatter();
}
Reason:
The method parseDefaulting(...) works in a funny way, namely like an embedded parser. That means, this method will inject a default value for defined field if that field has not been parsed yet. And the later pattern instruction tries to parse the same field (here: MONTH_OF_YEAR for pattern "MM/dd" and input "07/13") but with a possibly different value. If so then the composite parser will abort because it has found ambivalent values for same field and is unable to resolve the conflict (parsed value 7, but default value 1).
The official API contains following notice:
During parsing, the current state of the parse is inspected. If the
specified field has no associated value, because it has not been
parsed successfully at that point, then the specified value is
injected into the parse result. Injection is immediate, thus the
field-value pair will be visible to any subsequent elements in the
formatter. As such, this method is normally called at the end of the
builder.
We should read it as:
Dont't call parseDefaulting(...) before any parsing instruction for the same field.
Side note 1:
Your alternative approach based on parseBest(...) is even worse because
it does not cover all combinations with missing minute or only missing year (MonthDay?) etc. The default value solution is more flexible.
it is performancewise not worth to be discussed.
Side note 2:
I would rather have made the whole implementation order-insensitive because this detail is like a trap for many users. And it is possible to avoid this trap by choosing a map-based implementation for default values as done in my own time library Time4J where the order of default-value-instructions does not matter at all because injecting default values only happens after all fields have been parsed. Time4J also offers a dedicated answer to "What is the best strategy to enable dynamic date / time / date-time / zone-date-time parsing?" by offering a MultiFormatParser.
UPDATE:
In Java-8: Use ChronoField.YEAR_OF_ERA instead of ChronoField.YEAR because the pattern contains the letter "y" (=year-of-era, not the same as proleptic gregorian year). Otherwise the parse engine will inject the proleptic default year in addition to parsed year-of-era and will find a conflict. A real pitfall. Just yesterday I had fixed a similar pitfall in my time library for the month field which exists in two slightly different variations.
I have used new java.time package and it takes time getting used to it. But after a learning curve I have to say it is definitely very comprehensive and robust solution probably superseding Joda time library and other previous solutions. I wrote my own utilities for working with parsing Strings to Date. I wrote a summarizing article that explains how I implemented a feature that parsed String of unknown format to Date. It might be helpful. Here is the link to an article: Java 8 java.time package: parsing any string to date

How to remove the SECONDS field from a DateFormat

I want to print a time without seconds in the default format for the locale.
So I get the formatter with getTimeInstance() or getTimeInstance(int style). But even when I use a SHORT style it will contain the seconds part of the time as well.
Is there any way (apart from creating my own format, which then would not be the locale default and manually maintained) I can grab the default and split off the seconds ?
Thanks
Roman
DateFormat.getTimeInstance(DateFormat.SHORT) works perfectly fine here: from 20:00:00 to 20:00 and from 8:00:00 PM to 8:00 PM.
EDIT: This is insufficient (as stated by the first comment below). I'm keeping this here for the sake of history and to keep others from responding in a similar fashion :)
Have you considered saving the current format as a string and manually removing the seconds using String's substring method?
In case someone is reading this and either uses Java 8 or later or is fine with a (good and futureproof) external library:
DateTimeFormatter noSeconds = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
.withLocale(Locale.ITALY);
LocalTime time = LocalTime.now(ZoneId.systemDefault());
System.out.println(time.format(noSeconds));
This just printed:
15.26
Please substitute your desired locale instead of Locale.ITALY. Use Locale.getDefault() for your JVM’s locale setting. I believe it prints without seconds in all locales.
In the code I have used a LocalTime object, but the same code works for many other date and time classes including LocalDateTime, OffsetDateTime, OffsetTime and ZonedDateTime.
To use DateTimeFormatter and any of the other classes mentioned on Android you need the ThreeTenABP. More details on how to in this question: How to use ThreeTenABP in Android Project. For any non-Android Java 6 or 7, use ThreeTen Backport.

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