Date-String parsing problem (due to months from 0 to 11) - java

The code
String strDate = "2010-12-01";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
Date parsedDate = sdf.parse(strDate);
System.out.println(parsedDate);
will, dependend on your locale, produce the following output:
Fri Jan 01 00:12:00 CET 2010
The date is not parsed correctly, since i expect the 1st dec and not the 1st jan.
I know, months are numbered from 0 to 11, so the 12 becomes a 0 for january.
I have several solutions for this problem in mind, but all of them will produce at least 3-4 additional lines of code. So my question is:
What is the nicest way to solve this "problem"?
I can't imagine that it takes more than 2-3 lines to parse a simple date...
//edit: Shame on me for this question. Forgive me. thx folks

change yyyy-mm-dd to yyyy-MM-dd
M Month in year Month July; Jul; 07
m Minute in hour Number 30
See
SimpleDateFormat

Your date format is incorrect: Months are MM (not mm, which is for minutes). Try this:
"yyyy-MM-dd"
The reason you are getting January is that you haven't given a month to the parser (you gave year-minute-day). January, the first month, is the default month allocated to the date if not provided by the input. The 12 got parsed into the minute field (fairly obviously)

What is the nicest way to solve this "problem"?
Use different classes. You are using troublesome old legacy classes. Instead use the java.time classes.
LocalDate
The LocalDate class represents a date-only value without time-of-day and without time zone. It counts months sensibly, 1-12 is January through December.
Your input string is already in one of the ISO 8601 formats. These standard formats are used by default in the java.time classes. So no need with defining a formatting pattern.
LocalDate localDate = LocalDate.parse( "2010-12-01" );
Month
Also check out the handy Month enum.
Month month = Month.of( 1 ); // January = 1, December = 12.

Related

How to convert a String data and time including month name to Date object in Java?

I want to convert the date in string to date object being the string "10h 57m 20s October 13 2020". How can be done? may replace firstly the h, m and s to get the format "10:57:20 October 13 2020"? As well, I tried the last format "10:57:20 October 13 2020" to get the date with DateTimeFormat and DateTimeFormatterBuilder() but is does not work with the month or it works but the hour coverts to 00:00:00.
Thanks
java.time
I recommend that you use java.time, the modern Java date and time API, for your date and time work. Like Joop Eggen already wrote, put the letters that are part of your format in single quotes in the format pattern string:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("H'h' m'm' s's' MMMM d y", Locale.ENGLISH);
This will allow you to parse like this:
String dateInString = "10h 57m 20s October 13 2020";
LocalDateTime dateTime = LocalDateTime.parse(dateInString, FORMATTER);
System.out.println(dateTime);
Output:
2020-10-13T10:57:20
You shouldn’t take any interest in the old-fashioned Date class. However, sometimes we need to pass a Date to a legacy API not yet upgraded to java.time. The conversion requires that we know the time zone assumed for the parsed date and time. For example:
ZoneId zone = ZoneId.of("America/Tegucigalpa");
Instant i = dateTime.atZone(zone).toInstant();
Date oldfashionedDate = Date.from(i);
System.out.println(oldfashionedDate);
Example output:
Tue Oct 13 10:57:20 CST 2020
Tutorial link
Oracle tutorial: Date Time explaining how to use java.time.
You can place fixed letters in apostrophes.
"HH'h' mm'm' ss's' MMMM dd yyyy"
Furthermore hh is the 12 hour format to be combined wiht a AM/PM.
HH is the 24 hour format.
Also the locale must be correct, maybe explicitly set. Here English.

GregorianCalendar with a custom cutover date sets unexpected date before cutover

TL;DR: I'm getting a strange result when setting a GregorianCalendar with a custom Julian->Gregorian cutover date. 1 Jan 1800 becomes 12 Jan 1800, where 1800 is before the custom cutover date (31 Jan 1918) but after the default cutover date (15 Oct 1582). (This does not happen before the default cutover date or on the default calendar.)
This is part of a bigger algorithm where I want to use GregorianCalendar to calculate certain dates in a year, hoping to benefit by having the Julian/Gregorian leap year calculations be transparent to me, since the dates may be either before or after the cutover date.
I'm trying to use the following properties of GregorianCalendar, quoted from GregorianCalendar API docs:
GregorianCalendar is a hybrid calendar that supports both the Julian and Gregorian calendar systems with the support of a single discontinuity, which corresponds by default to the Gregorian date when the Gregorian calendar was instituted (October 15, 1582 in some countries,
later in others). The cutover date may be changed by the caller by calling setGregorianChange().
Before the Gregorian cutover, GregorianCalendar implements the Julian calendar. The only difference between the Gregorian and the Julian calendar is the leap year rule.
Prior to the institution of the Gregorian calendar, New Year's Day was March 25. To avoid confusion, this calendar always uses January 1.
To replicate the base problem, here is a basic Java main() method. I create 2 calendars, one the default Gregorian, and another one a Gregorian with the cutover date when Russia adopted it, i.e. 31 Jan 1918. Then I set both calendars to 1 Jan 1800. The "Russian" calendar changes this date to 12 Jan 1800, as shown when printed out immediately after the set.
public static void main(String[] args) {
DateFormat DF = DateFormat.getDateInstance(DateFormat.SHORT);
System.out.printf("Generic Gregorian Calendar (after cutover)%n");
GregorianCalendar genericCal = new GregorianCalendar();
System.out.printf(" Changeover=%s%n", DF.format(genericCal.getGregorianChange()));
genericCal.set(1800, Calendar.JANUARY, 1);
System.out.printf("%3s %s%n", "", DF.format(genericCal.getTime()));
System.out.printf("Russian Gregorian Calendar (before cutover)%n");
GregorianCalendar russianCal = new GregorianCalendar();
russianCal.setGregorianChange(new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
System.out.printf(" Changeover=%s%n", DF.format(russianCal.getGregorianChange()));
russianCal.set(1800, Calendar.JANUARY, 1);
System.out.printf("%3s %s%n", "", DF.format(russianCal.getTime()));
for (int i = 1; i < 15; i++) {
russianCal.add(Calendar.DAY_OF_YEAR, -1);
System.out.printf("%3d: %s %n", -i, DF.format(russianCal.getTime()));
}
}
This outputs:
Generic Gregorian Calendar (after cutover)
Changeover=1582/10/15
1800/01/01
Russian Gregorian Calendar (before cutover)
Changeover=1918/01/31
1800/01/12
-1: 1800/01/11
-2: 1800/01/10
-3: 1800/01/09
-4: 1800/01/08
-5: 1800/01/07
-6: 1800/01/06
-7: 1800/01/05
-8: 1800/01/04
-9: 1800/01/03
-10: 1800/01/02
-11: 1800/01/01
-12: 1799/12/31
-13: 1799/12/30
-14: 1799/12/29
The 11-day difference looks similar to the days lost when the Julian/Gregorian switchover would be made, but that would only apply during the period in 1918 after 31 Jan, in this instance (31 Jan was followed by 14 Feb in 1918 in Russia, which is a 13-day difference).
I would appreciate any explanation, or help to get the date set to what I intend.
Unfortunately I'm stuck with standard Java 8 libraries, no 3rd party libraries possible at this time. Also, it seems the new java.time classes won't "automatically" help with the Julian/Gregorian transition (if I'm wrong I welcome pointers), so my Plan B would be to simply do the calculations without using any date classes.
Your code behaves correctly though confusingly, I agree. For formatting your Russian date you need to instruct your formatter to use the Russian Gregorian crossover date.
Setting your Russian calendar to 1 Jan 1800 works. There is no change happening.
GregorianCalendar russianCal = new GregorianCalendar();
russianCal.setGregorianChange(
new GregorianCalendar(1918, Calendar.JANUARY, 31).getTime());
russianCal.set(1800, Calendar.JANUARY, 1);
System.out.println("Russian month (0-based): "
+ russianCal.get(Calendar.MONTH));
System.out.println("Russian day of month: "
+ russianCal.get(Calendar.DATE));
Output is:
Russian month (0-based): 0
Russian day of month: 1
The date is 1 Jan as it should (Calendar confusingly uses 0 for January).
When you take out the time into a Date, you get a normal java.util.Date, there’s nothing Russian nor Julian about it. The Date object represents some time of the day that is 1 Jan in Russia and 12 Jan in the Gregorian calendar, in your default time zone. When you further format this Date using a DateFormat with default settings, is uses the standard Gregorian crossover date in 1582 and therefore prints 12 Jan. To print 1 Jan as in the Russian calendar you need to instruct the formatter to use the Russian Gregorian crossover date. You do this by passing it a GregorianCalendar that uses the desired crossover date:
Date dateCorrespondingToRussianCal = russianCal.getTime();
System.out.println("Corresponding java.util.Date: "
+ dateCorrespondingToRussianCal);
DateFormat russianDateFormatter
= DateFormat.getDateInstance(DateFormat.SHORT);
russianDateFormatter.setCalendar(russianCal);
System.out.println("Formatted Russian date: "
+ russianDateFormatter.format(dateCorrespondingToRussianCal));
Output in my time zone:
Corresponding java.util.Date: Sun Jan 12 13:10:30 CET 1800
Formatted Russian date: 1/1/00
Other options?
Unfortunately you are correct: java.time, the modern Java date and time API, has no support for the Julian calendar out of the box. I read that you could not use any 3rd party libraries. Options to consider include:
For other readers that may use a 3rd party library: Joda-Time and it GJChronology. It
Implements the Gregorian/Julian calendar system which is the calendar system used in most of the world. …
For yourself: You may develop your own Julian-Gregorian chronology to use with java.time. It would require an effort, but I would expect it to give a beautiful result.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Documentation of Joda-Time’s GJChronology

how to convert Julian date (ordinal date) in Java [duplicate]

This question already has answers here:
Julian day of the year in Java
(9 answers)
Closed 2 years ago.
Requirement : get the date in Julian format (ordinal date), compare it with current date, get the month and year out of it.
Convert it with output format.
Input format: yydddd
Output format: yymm
JDK 8
One way I can do is using date :
Date myDateWrong = new SimpleDateFormat("yyyyddd").parse("2020366");
Any cleaner way ?
java.time
looking for java.time.* solution which can parse yyyyddd format
That’s what I recommend too.
DateTimeFormatter dayOfYearFormatter
= DateTimeFormatter.ofPattern("uuuuDDD");
DateTimeFormatter yearMonthFormatter
= DateTimeFormatter.ofPattern("uuMM");
String yyyydddString = "2020366";
LocalDate date = LocalDate.parse(yyyydddString, dayOfYearFormatter);
String output = date.format(yearMonthFormatter);
System.out.println(output);
Output is:
2012
So year 2020 month 12.
What went wrong in your code?
Whether you use the modern DateTimeFormatter or the old and troublesome SimpleDateFormat, lowercase d is for day of month and uppercase D is for day of year. Why it worked with SimpleDateFormat anyway was because that class confusingly defaults month to January if no month is given. So your date was parsed into the 366th day of January. What?! That’s right, one more confusing trait of SimpleDateFormat, with default settings it happily parses non-existent dates. When there are only 31 days in January, it just extrapolates into the following months and ends up at December 31, the day you had intended. SimpleDateFormat is so full of nasty surprises like these. I recommend you never ever use that class again.
Link
Oracle tutorial: Date Time explaining how to use java.time.

Calendar.getTime IllegalArgumentException [duplicate]

This question already has answers here:
Android Calendar illegalArgumentException when calendar.month set to 1
(2 answers)
Closed 3 years ago.
import java.util.Calendar;
public class WeekYear {
static String input = "202001";
//static String format = "YYYYMM";
public static void main(String[] args) throws ParseException {
Calendar lCal = Calendar.getInstance();
System.out.println(lCal.isLenient());
lCal.setLenient(false);
lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//lCal.setMinimalDaysInFirstWeek(5);
System.out.println(lCal.isLenient());
System.out.println(lCal.getTime());
//lCal.set(Calendar.YEAR, new Integer(input.substring(0, 4)).intValue());
//lCal.set(Calendar.WEEK_OF_YEAR, new Integer(input.substring(4, 6)).intValue());
//System.out.println(lCal.getTime());
}
}
When this code is executed on Nov 22nd, 2020 I get an IllegalArgumentException from Calendar.getTime(). But when executed on Nov 27, 2020 it works fine.
The documentation says:
The setLenient(boolean leniency) method in Calendar class is used to specify whether the interpretation of the date and time is to be lenient or not. Parameters: The method takes one parameter leniency of the boolean type that refers to the mode of the calendar.
Any explanation? I am not able to reproduce the issue even in my local now. Local time is set to CST
Exception Stack:
Exception in thread "main" java.lang.IllegalArgumentException: year: 2020 -> 2019
at java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2829)
at java.util.Calendar.updateTime(Calendar.java:3393)
at java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.util.Calendar.getTime(Calendar.java:1755)
at WildDog.main(WildDog.java:13)
`````````
tl;dr
Never use Calendar, now legacy, supplanted by java.time classes such as ZonedDateTime.
Use a purpose-built class, YearWeek from the ThreeTen-Extra project, to track standard ISO 8601 weeks.
Custom formatter
Define a DateTimeFormatter object to match your non-standard input string.
org.threeten.extra.YearWeek
.parse(
"202001" ,
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
)
.toString()
2020-W01
Standard formatter
Or manipulate your input string to comply with the ISO 8601 standard format, inserting a -W in the middle between the week-based-year and the week. The java.time classes and the ThreeTen-Extra classes all use the ISO 8601 formats by default when parsing/generating strings.
String input = "202001";
String inputModified = input.substring( 0 , 4 ) + "-W" + input.substring( 4 );
YearWeek yearWeek = YearWeek.parse( inputModified ) ;
yearWeek.toString(): 2020-W01
Avoid legacy date-time classes
Do not waste your time trying to understand Calendar. This terrible class was supplanted years ago by the modern java.time classes defined in JSR 310.
Definition of week
You must specify your definition of a week. Do you mean week number 1 contains the first day of the year? Or week # 1contains a certain day of the week? Or week # 1 is the first calendar week to consist entirely of dates in the new year? Or perhaps an industry-specific definition of week? Some other definition?
One of the confusing things about Calendar is that its definition of a week shifts by Locale. This one of many reasons to avoid that legacy class.
Week-based year
Depending on your definition of week, the year of a week may not be the calendar year of some dates on that week. A week-based year may overlap with calendar years.
Standard weeks and week-based year
For example, the standard ISO 8601 week defines a week as:
Starting on Monday, and
Week # 1 contains the first Thursday of the calendar year.
So there are 52 or 53 whole weeks in every week-based year. Of course, that means some dates from the previous and/or following calendar years may appear in the first/last weeks of our week-based year.
org.threeten.extra.YearWeek
One problem is that you are trying to represent a year-week with a class that represents a moment, a date with time of day in the context of a time zone.
Instead, use a purpose-built class. You can find one in the ThreeTen-Extra library, YearWeek. This library extends the functionality of the java.time classes built into Java 8 and later.
With that class I would think that we could define a DateTimeFormatter to parse your input using the formatting pattern YYYYww where the YYYY means a 4-digit year of week-based-year, and the ww means the two-digit week number. Like this:
// FAIL
String input = "202001" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "YYYYww" ) ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
But using that formatter throws an DateTimeParseException for reasons that escape me.
Exception in thread "main" java.time.format.DateTimeParseException: Text '202001' could not be parsed: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.DateTimeException: Unable to obtain YearWeek from TemporalAccessor: {WeekOfWeekBasedYear[WeekFields[SUNDAY,1]]=1, WeekBasedYear[WeekFields[SUNDAY,1]]=2020},ISO of type java.time.format.Parsed
…
Caused by: java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: WeekBasedYear
Alternatively, we can use DateTimeFormatterBuilder to build up a DateTimeFormatter from parts. By perusing the OpenJDK source code for Java 13 for DateTimeFormatter.ISO_WEEK_DATE I was able to cobble together this formatter that seems to work.
DateTimeFormatter f =
new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue( IsoFields.WEEK_BASED_YEAR, 4, 10, SignStyle.EXCEEDS_PAD)
.appendValue(IsoFields.WEEK_OF_WEEK_BASED_YEAR, 2)
.toFormatter()
;
Using that:
String input = "202001" ;
YearWeek yearWeek = YearWeek.parse( input , f ) ;
ISO 8601
Educate the publisher of your data about the ISO 8601 standard defining formats for representing date-time values textually.
To generate a string in standard format representing the value of our YearWeek, call toString.
String output = yearWeek.toString() ;
2020-W01
And parsing a standard string.
YearWeek yearWeek = YearWeek.parse( "2020-W01" ) ;
Basil Bourque has already provided a very good answer. Here’s one that doesn’t require an external dependency (provided you are using Java 8 or later).
java.time
WeekFields wf = WeekFields.of(Locale.US);
DateTimeFormatter yearWeekFormatter = new DateTimeFormatterBuilder()
.appendValue(wf.weekBasedYear(), 4)
.appendValue(wf.weekOfWeekBasedYear(), 2)
.parseDefaulting(ChronoField.DAY_OF_WEEK, DayOfWeek.SUNDAY.getValue())
.toFormatter();
String input = "202001";
LocalDate date = LocalDate.parse(input, yearWeekFormatter);
System.out.println(date);
Output is:
2019-12-29
Assuming American weeks where Sunday is the first day of the week and week 1 is the week containing January 1, this is correct: week 1 of 2020 begins on Sunday, December 29, 2019. If you want weeks defined in some other way, just use a different WeekFields object.
I recommend that you don’t use the Calendar class. That class was always poorly designed and is now long outdated. Instead I am using java.time, the modern Java date and time API.
Any explanation?
With thanks to user85421 for how to reproduce. You are first creating a Calendar object (really an instance of GregorianCalendar) representing the current day, in your example Nov 22nd, 2020, a Sunday (apparently having set your computer clock nearly a year ahead). You are then setting its year to 2020 (no change) and its week number to 1. However, as we saw above, this would change the date to December 29, 2019, and thus create a conflict with the year that you set to 2020. Therefore GregorianCalendar decides that you are asking the impossible and throws the exception. The stack trace that I got was:
java.lang.IllegalArgumentException: YEAR: 2020 -> 2019
at java.base/java.util.GregorianCalendar.computeTime(GregorianCalendar.java:2826)
at java.base/java.util.Calendar.updateTime(Calendar.java:3395)
at java.base/java.util.Calendar.getTimeInMillis(Calendar.java:1782)
at java.base/java.util.Calendar.getTime(Calendar.java:1755)
at ovv.misc.Test.main(Test.java:17)
In your second example you were running your program on Nov 27, 2020, a Friday. This time the date is changed to Friday, January 3, 2020, so still within year 2020, and therefore there is no conflict and hence no exception.
The explanation presumes that your default locale is one where week 1 of the year is defined as the week that contains January 1. I have ran your code in my own locale after setting my computer’s time to Nov 22, 2020, and my time zone to America/Chicago. No exception was seen (output included Sun Jan 05 13:54:27 CST 2020). My locale follows the international standard, ISO. Monday is the first day of the week, and week 1 is the first week that has at least 4 days of the new year in it. So week 1 of 2020 is from Monday, December 30, 2019, through Sunday, January 5. I suppose that on a Monday or Tuesday I could reproduce your problem in this locale too, I haven’t tried.
PS How to parse an integer
Just a tip, to parse a string into an int, just use Integer.parseInt(yourString). No need to create a new Integer object.
Link
Oracle tutorial: Date Time explaining how to use java.time.

How to print date 9999-99-99 in xmlgregoriancalendar?

Need to print the date exactly as "9999-99-99" using xmlgregoriancalendar type.
When i pass "9999-99-99" i get wrong output: 10007-07-08. How do i get output exactly as 9999-99-99
import java.util.GregorianCalendar;
import javax.xml.datatype.DatatypeFactory;
public class XMLGregorianCalendar {
public static void main(String[] args) {
/* Create Date Object */
//Date date = new Date();
javax.xml.datatype.XMLGregorianCalendar xmlDate = null;
//GregorianCalendar gc = new GregorianCalendar(2001,12,12);
GregorianCalendar gc = new GregorianCalendar(9999,99,99);
// gc.setTime(date);
try{
xmlDate = DatatypeFactory.newInstance().newXMLGregorianCalendar(gc);
}catch(Exception e){
e.printStackTrace();
}
System.out.println("XMLGregorianCalendar :- " + xmlDate);
}
}
Don’t
The XMLGregorianCalendar class was for dates and/or times for XML documents. Assuming that this was also your purpose, you must not put 9999-99-99 there. It’s not a valid date according to XML rules. Quoting XML Schema Part 2: Datatypes Second Edition, appendix D ISO 8601 Date and Time Formats:
M -- represents a digit used in the time element "month". The two digits in a MM format can have values from 1 to 12.
D -- represents a digit used in the time element "day". The two digits in a DD format can have values from 1 to 28 if the month value
equals 2, 1 to 29 if the month value equals 2 and the year is a leap
year, 1 to 30 if the month value equals 4, 6, 9 or 11, and 1 to 31 if
the month value equals 1, 3, 5, 7, 8, 10 or 12.
I have taken it out of context, but I think that we should understand that dates in XML documents need to be valid dates. 9999-99-99 is not a valid date since there is no month 99 and no month has 99 days in it.
If you wanted 9999-99-99 for something else than an XML document, I don’t think you should be using XMLGregorianCalendar at all. Without context I dare not suggest alternatives.
java.time I said “was … for XML documents”. Dates and times in XML documents are inspired from ISO 8601 formats and close enough that we usually can use the classes from java.time, the modern Java date and time API rather than XMLGregorianCalendar for them and still get the correct syntax from the toString methods of those classes. So also for valid dates consider using the modern LocalDate from java.time rather than the old XMLGregorianCalendar.
You cannot
XMLGregorianCalendar imposes the restriction of a valid date, so cannot print 9999-99-99.
What happened in your code was that GregorianCalendar tacitly and confusingly modified the date into a valid one. Try for example:
GregorianCalendar gc = new GregorianCalendar(9999,99,99);
System.out.println(gc.getTime());
On my computer I got:
Sun Jul 08 00:00:00 CEST 10007
When given invalid month and day of month, GregorianCalendar just keeps counting months and days into the following years. Since 99 months is a little more than 8 years, we end up more than 8 years after January 9999, and a further 3 months because of the 99 days. This was then the date that you passed to your XMLGregorianCalendar, which explains the output you got.
Links
XML Schema Part 2: Datatypes Second Edition, appendix D ISO 8601 Date and Time Formats
Wikipedia article: ISO 8601
Oracle tutorial: Date Time explaining how to use java.time.

Categories