This question already has answers here:
Why does Java's java.time.format.DateTimeFormatter#format(LocalDateTime) add a year?
(2 answers)
Using DateTimeFormatter on january first cause an invalid year value
(1 answer)
Closed 1 year ago.
The following code (in main)
LocalDate d ;
DateTimeFormatter formatter;
d = LocalDate.of(2021, 11, 14);
System.out.println(d);
formatter = DateTimeFormatter.ofPattern("MM.dd.YYYY");
System.out.println(formatter.format(d));
formatter = DateTimeFormatter.ofPattern("YYYY/MM/dd");
System.out.println(formatter.format(d)+ "\n");
d = LocalDate.of(2021, 1, 1);
System.out.println(d);
formatter = DateTimeFormatter.ofPattern("MM.dd.YYYY");
System.out.println(formatter.format(d));
formatter = DateTimeFormatter.ofPattern("YYYY/MM/dd");
System.out.println(formatter.format(d));
outputs in my Netbeans 12.5 with the latest or fairly recent Oracle Java 17
run:
2021-11-14
14.11.2021
2021/11/14
2021-01-01
01.01.2020
2020/01/01
BUILD SUCCESSFUL (total time: 0 seconds)
The formatted output for today, 14-NOV-2021, is ok, but output for 01-JAN-2021 is wrong. Formatted output prints 2020 , one year off.
Can you repeat this? If so: Any ideas why this happens?
According to the Javadoc of java.time.format.DateTimeFormatter
y (lowercase) represents the year-of-era and
Y (uppercase) represents the week-based-year.
The week-based date/time system is defined in ISO 8601 (see: Wikipedia article).
You want u, not Y:
Y is week-based year. If you're an accountant, this is important. If you're not, this is the devil: It seems right almost always, except in certain highly exotic dates, such as jan 1st. Weeks customarily are defined as: "Whatever year it is on the thursday of that week, that's the year that week belongs to", so if e.g. Jan 1st falls on a friday, then the week that contains Jan 1st is of the previous year (as the thursday of that week was dec 31st).
y is year as you mostly know it, except it doesn't really do what you want when go to BC years. It's not likely to come up, but then neither is the week-based-year thing. Nobody likes Year-2k style bugs, right?
u truly does what you imagined. It just gives you the year. Negative if need be.
Related
I have some strings in Java that come in the format: Day Month Year Hour:Minute:Second
7 Jan 2010 23:00:00.000
4 Feb 2010 17:40:00.000
What is the easiest way to parse this string and convert the values to their resulting Julian Dates? I am reading in these strings from Excel so they are not objects with any sort of conversion/formatting utilities (just raw strings). Is there an easy library or function to call to convert these, or would I have to manually write a parser? Thanks
java.time
Sure, Java has got a parser for date and time built-in, the DateTimeFormatter class (named so because it can also format date and time back to strings). And a number of classes that can utilize it for producing objects of themselves. In your case you need the LocalDateTime class. A LocalDateTime is a date and time of day without time zone or offset from UTC, so appropriate for holding the data from your string.
This formatter s good for your string:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("d MMM uuuu HH:mm:ss.SSS", Locale.ENGLISH);
Edit: You wrote in a comment:
Plugging in Jan 7 2010 hour 23 into this calculator:
aavso.org/jd-calculator gives
back 2455204.45833. Would this be the exact Julian Date? I believe
your solution was giving the Day instead of Date decimal value
Yes, that’s exactly true. The modified code to get the julian date including the fraction is:
String source = "7 Jan 2010 23:00:00.000";
LocalDateTime ldt = LocalDateTime.parse(source, FORMATTER);
// Subtract half a day to compensate for the
// fact that the Julian day begins at noon
LocalDateTime dateToUseForJulianDay = ldt.minusHours(12);
long julianDayNumber = dateToUseForJulianDay.getLong(JulianFields.JULIAN_DAY);
double juianDateFraction = (double) dateToUseForJulianDay.getLong(ChronoField.NANO_OF_DAY)
/ (double) Duration.ofDays(1).toNanos();
double julianDate = julianDayNumber + juianDateFraction;
System.out.println("Julian date: " + julianDate);
And the output is in this example:
Julian date: 2455204.4583333335
It agrees very nicely with thee result you quote from the online calculator.
The Julian day number is the day number since January 1, 4713 BC. The Julian day starts at noon, which Java does not take into account, so as a hack I have subtracted 12 hours to compensate and get the correct day for all times of day. Since the getLong() method only gets the Julian day number as a whole number, I need to find the fraction separately. It’s a matter of dividing the nanosecond of the day by the total number of nanoseconds in a day. From the original date and time we would have needed the number of nanos since 12 noon; but since I have already subtracted 12 hours, the nanosecond of the day, since 0:00 midnight, is the number we need.
Further link: Julian day on Wikipedia
My library Time4J supports Julian Dates out of the box.
ChronoFormatter<PlainTimestamp> f =
ChronoFormatter.ofTimestampPattern(
"d MMM uuuu HH:mm:ss.SSS", PatternType.CLDR, Locale.ENGLISH);
Moment j2000 = f.parse("7 Jan 2010 23:00:00.000").atUTC(); // are your timestamps really UTC?
// eventually also: ".in(Timezone.ofSystem());"
System.out.println(JulianDay.ofSimplifiedTime(j2000)); // programmer's standard
// JD(POSIX)2455204.4583333335
System.out.println(JulianDay.ofEphemerisTime(j2000)); // astronomical definition
// JD(TT)2455204.459099352
Advantages:
No complex calculation of your owns.
Support for the astronomical definition on the time scale TT.
Explicit display of time zone dependencies (whatever you choose).
I am using calendar object in java to convert an input date(year/month/date etc are getting from network ) to epoch time. I am reusing same calendar object . Sometimes the year i am getting from
network is 0 and there was no validation for this once. Once this happens the whenever i convert the date to epoch, the epoch time im getting is always negative. Is this a valid behaviour
Please find sample code and result I got while doing a unit test for this issue.
Calendar cal= Calendar.getInstance();
cal.set(Calendar.YEAR, 0);
System.out.println("time 1 : "+cal.getTimeInMillis());
System.out.println("Date 1 : "+new Date(cal.getTimeInMillis()));
cal.set(Calendar.YEAR, 2020);
System.out.println("time 2 : "+cal.getTimeInMillis());
System.out.println("Date 2 : "+new Date(cal.getTimeInMillis()));
cal.set(Calendar.YEAR, 2010);
cal.set(Calendar.MONTH, 10);
System.out.println("time 3 : "+cal.getTimeInMillis());
System.out.println("Date 3 : "+new Date(cal.getTimeInMillis()));
Output for this code is
Time 1 : -62151385126938
Date 1 : Sun Jul 04 11:51:13 IST 1
Time 2 : -125866201126938
Date 2 : Wed Jul 04 11:51:13 IST 2020
Time 3 : -125540041126938
Date 3 : Fri Nov 04 11:51:13 IST 2010
Is this an expected behaviour for java Calendar?
My JDK version is
OpenJDK Runtime Environment Corretto-8.252.09.1 (build 1.8.0_252-b09)
Edit:
There are other option/class to fix this issue, But my purpose is to find the root cause of this behaviour
I am using calendar object
Stop doing that. Never use the terrible Calendar and Date classes. Use only the modern java.time classes defined in JSR 310.
Sometimes the year i am getting from network is 0 and there was no validation for this once. Once this happens the whenever i convert the date to epoch, the epoch time im getting is always negative. Is this a valid behaviour
Yes, a negative count of milliseconds since the epoch reference of first moment of 1970 in UTC would be correct for a date with year zero. Year 0000 occurred over two thousands years ago, so that would be a very large number of milliseconds counting backwards from 1970-01-01T00:00Z.
I understand that you see Calendar returning bizarre negative numbers after resetting for contemporary years. If you want more info, see excellent Answer by Ole V.V. In my opinion: there is no purpose to investigating Calendar now that we have java.time. Let’s bury the dead, and move on.
If you must inter-operate with old code not yet updated for java.time, you can convert back and forth between the legacy classes and the modern. Look to new methods added to the old classes. Minimize your use of the old legacy classes.
if( myCalendar instanceof GregorianCalendar )
{
ZonedDateTime zdt = ( ( GregorianCalendar ) myCalendar ).toZonedDateTime() ;
}
…and…
Calendar myCalendar = GregorianCalendar.from( zdt ) ;
You said:
I am reusing same calendar object .
Stop doing that. Reusing objects of that class leads to various problems.
One of the important design considerations in java.time is the use of only immutable objects, to avoid these reuse problems.
am using calendar object in java to convert an input date(year/month/date
You do not show us this code, so I cannot help you any further.
Search Stack Overflow before posting. All this has been covered many times already.
Is this an expected behaviour for java Calendar?
My perhaps polemic answer is that Calendar pretty often behaves contrary to expectations. It has been designed and documented to do so. I understand your confusion.
There is no year 0. So a first expectation might have been that Calendar should throw an exception when you try to set the year of era to 0. With standard settings it doesn’t. Instead it extrapolates: year 0 is taken to mean the year before year 1 , so that is year 1 BC. In your output you can see that the year is 1 (not 0); but what you cannot see is the era, it’s BC, not AD (CE). So let’s add a couple of lines that print the era to your code:
Calendar cal= Calendar.getInstance();
System.out.format("Initial: Era %s year %d%n",
cal.getDisplayName(Calendar.ERA, Calendar.SHORT_STANDALONE, Locale.ENGLISH),
cal.get(Calendar.YEAR));
cal.set(Calendar.YEAR, 0);
System.out.format("Year 0: Era %s year %d%n",
cal.getDisplayName(Calendar.ERA, Calendar.SHORT_STANDALONE, Locale.ENGLISH),
cal.get(Calendar.YEAR));
System.out.println("time 1 : "+cal.getTimeInMillis());
System.out.println("Date 1 : "+new Date(cal.getTimeInMillis()));
When I ran the code just now, the output was:
Initial: Era AD year 2020
Year 0: Era BC year 1
time 1 : -62151371618330
Date 1 : Sun Jul 04 11:06:21 CET 1
Next, Calendar.YEAR means year of era, at least to a GregorianCalendar (which is the concrete subclass of Calendar of which you got an instance from Calendar.getInstance()). So when next you set YEAR, that is, year of era, to 2020, the Calendar stays before Christ (before the common era), so you get 2020 BC, more than 4000 years ago. And your milliseconds “grow” to approximately double magnitude of the already negative value.
cal.set(Calendar.YEAR, 2020);
System.out.format("Year 2020: Era %s year %d%n",
cal.getDisplayName(Calendar.ERA, Calendar.SHORT_STANDALONE, Locale.ENGLISH),
cal.get(Calendar.YEAR));
System.out.println("time 2 : "+cal.getTimeInMillis());
System.out.println("Date 2 : "+new Date(cal.getTimeInMillis()));
Year 2020: Era BC year 2020
time 2 : -125866187618330
Date 2 : Wed Jul 04 11:06:21 CET 2020
IMHO it’s a fine case for avoiding the Calendar class.
java.time
And yes, if only for other readers, there are alternatives.
First, you should not try to handle a meaningless year. I don’t know your situation and requirements, so cannot tell you what to do when you get a year that is 0 or negative, but you need to decide, and you need to check and act appropriately. What you have been doing until now is wrong, as you are aware.
Second, very obviously you should use java.time, the modern Java date and time API.
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.systemDefault());
zdt = zdt.withYear(0);
System.out.println(zdt);
System.out.println(zdt.toEpochSecond());
zdt = zdt.withYear(2020);
System.out.println(zdt);
System.out.println(zdt.toEpochSecond());
zdt = zdt.withYear(2010);
System.out.println(zdt);
0000-07-04T11:23:08.573357+00:50:20[Europe/Copenhagen]
-62151197232
2020-07-04T11:23:08.573357+02:00[Europe/Copenhagen]
1593854588
2010-07-04T11:23:08.573357+02:00[Europe/Copenhagen]
You notice in this output that the seconds since the epoch turn positive again after they have been negative. In java.time a year means the proleptic year, that is, a signed year. Proleptic year 0 is 1 BC, -1 means 2 BC, etc. So year 0 is just any other year and doesn’t incur any weird behaviour.
Also java.time does object to setting year of era to 0, which might have helped you discover earlier that you had an issue:
zdt = zdt.with(ChronoField.YEAR_OF_ERA, 0);
Exception in thread "main" java.time.DateTimeException: Invalid value for YearOfEra (valid values 1 - 999999999/1000000000): 0
at java.base/java.time.temporal.ValueRange.checkValidValue(ValueRange.java:311)
at java.base/java.time.temporal.ChronoField.checkValidValue(ChronoField.java:717)
at java.base/java.time.LocalDate.with(LocalDate.java:1048)
at java.base/java.time.LocalDateTime.with(LocalDateTime.java:970)
at java.base/java.time.ZonedDateTime.with(ZonedDateTime.java:1312)
at ovv.so.date.special.SetCalendarYear0.main(SetCalendarYear0.java:25)
This question already has answers here:
How to subtract n days from current date in java? [duplicate]
(5 answers)
Closed 7 years ago.
I'm trying to subtract 5 days from a date which comes in as a string initially.
I have had a look at some of the other posts on this subject but the result i get from the code is always incorrect. The main problem is that the year value does not seem to change when the days are subtracted for example - 2012-01-01 subtract 5 days gives me 'Jan 27 2012' using this code -
cal.add(Calendar.DATE, -5);
Please help.
Did you know that, in Java, month 1 is actually February?
Date februaryTheFirst = new Date(2012,1,1); // equals 2012-02-01
This might explain what you are seeing. If you want to instantiate 2012-01-01 instead, you should do:
Date firstDayOf2012 = new Date(2012,0,1); // this is 2012-01-01
Exactly the same thing happens when dealing with Calendar:
Calendar.getInstance().set(2012,0,1); // 2012-01-01
Be sure to check the documentation for Date(int, int, int) and Calendar.set(int, int, int). Also, you could check the way you are parsing the string. If you use SimpleDateFormat.parse(...), things can be easier.
Strange, isn't it? Go figure... Just as a fun fact, IntelliJ's documentation annotates this second parameter, month, with #MagicConstant, to remember the programmer that there's something very strange going on.
Calendar.FEBRUARY is 1 and five days before 1 Feb 2012 was 27 Jab 2012.
Your implementation is correct and you are getting the correct value aslo.
Calendar's Months started with 0
0 = Jan
1 = Feb
so subtracting 5 days from 2012-01-01 will definitely returns you Jan 27 2012
something is here also which will helps you Why is January month 0 in Java Calendar?
Joda-Time
The Joda-Time 2.7 library makes this work much easier. Just call the minusDays method.
String input = "2012-01-01";
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" );
DateTime dateTime = new DateTime( input, zone );
DateTime then = now.minusDays( 5 );
DateTimeFormatter formatter = DateTimeFormat.forStyle( "FF" ).withZone( zone ).withLocale( Locale.CANADA_FRENCH );
String output = formatter.print( then );
If you want the beginning of the day, add a call to withTimeAtStartOfDay. This is unnecessary in your case, when parsing a date-only string with no time-of-day.
DateTime dateTimeAtStartOfDay = new DateTime( input, zone ).withTimeAtStartOfDay();
If you want only date without time-of-day or time zone, use LocalDate instead of DateTime.
LocalDate then = new LocalDate( "2012-01-01" ).minusDays( 5 );
If you need to convert to the old java.util.Date, call toDate on the DateTime.
java.time
Java 8 has a new package, java.time. These new classes were inspired by Joda-Time but were re-architected. Both java.time and Joda-Time can solve this particular problem equally well.
Use:
cal.add(Calendar.DAY_OF_MONTH, -5)
EDIT: sorry. DAY_OF_MONTH is a synonym to DATE. Instead of 1 use Calendar.JANUARY.
This a segment of code that is working on my pc. first you have to get the calendar instance the perform your calculation.
Calendar cal = Calendar.getInstance();
System.out.println("Today : " + cal.getTime());
// Subtract 300 days from the calendar
cal.add(Calendar.DATE, -300);
System.out.println("300 days ago: " + cal.getTime());
This is the output that you will get:
Today : Wed Oct 17 10:41:23 EET 2012
300 days ago: Thu Dec 22 10:41:23 EET 2011
When I parse a date with the year 0000 it appears to be stored as the year 0001.
See below for code:
String dateStr = "00000102";
System.out.println(dateStr);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
Date date = dateFormat.parse("00000102");
String convertedStr = dateFormat.format(date);
System.out.println(convertedStr);
The output is as per below:
00000102
00010102
Is there a way to represent the year 0000 in Java using the standard Java API?
I don't believe it's possible, since java.util.Date is based on UTC, which is based on the Gregorian calendar, and the Gregorian calendar has no year zero.
...the traditional proleptic Gregorian calendar (like the Julian calendar) does not have a year 0 and instead uses the ordinal numbers 1, 2, … both for years AD and BC. Thus the traditional time line is 2 BC, 1 BC, AD 1, and AD 2.
(Source: The Wikipedia article on the Gregorian calendar)
I don't think the calendar is zero-based. Before 1 AD there was 1 BC. No 0.
Also: what kind of application are you building that needs to handle dates from that era? And if you need to cover that area, consider this: "Dates obtained using GregorianCalendar are historically accurate only from March 1, 4 AD onward, when modern Julian calendar rules were adopted. Before this date, leap year rules were applied irregularly, and before 45 BC the Julian calendar did not even exist."
Year 0 does not exist in the Gregorian calendar. From Year 0 at Wikipedia:
"Year zero" does not exist in the widely used Gregorian calendar or in its predecessor, the Julian calendar. Under those systems, the year 1 BC is followed by AD 1.
...
The absence of a year 0 leads to some confusion concerning the boundaries of longer decimal intervals, such as decades and centuries. For example, the third millennium of the Gregorian calendar began on Monday, 1 January, 2001, rather than the widely celebrated Saturday, 1 January, 2000. Likewise, the 20th century began on 1 January 1901.
...
java.time
I recommend that you use java.time, the modern Java date and time API, for your date work.
As others have said, the Julian/Gregorian calendar that the old Date and SimpleDateFormat classes used does not have a year zero. Before year one in the current era (CE also known as AD) came year 1 before the current era (BCE also known as BC).
A side effect of using java.time is that you do get a year zero! That’s right. java.time uses the proleptic Gregorian calendar, a modern inventions that not only extends the rules of the Gregorian back into times before the Gregorian calendar was invented, but also includes a year 0 before year 1, and a year -1 (minus one) before that. You may say that year 0 corresponds to 1 BCE and -1 to 2 BCE, etc.
So parsing your string is no problem. There’s even a built-in formatter for it.
String dateStr = "00000102";
LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.BASIC_ISO_DATE);
System.out.println("Parsed date is " + date);
String convertedStr = date.format(DateTimeFormatter.BASIC_ISO_DATE);
System.out.println(convertedStr);
Output:
Parsed date is 0000-01-02
00000102
We see that in both output lines year 0000 is printed back as expected.
What went wrong in your code?
When we all agree that there was no year 0, we should have expected your parsing to fail with an exception because of the invalid year. Why didn’t it? It’s one of the many problems with the old SimpleDateFormat class: with default settings it just extrapolates and takes year 0000 to mean the year before year 0001, so year 1 BCE. And falsely pretends that all is well. This explains why year 0001 was printed back: it meant year 1 BCE, but since you didn’t print the era too, this was really hard to tell.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Proleptic Gregorian calendar on Wikipedia.
I am doing this..
String dateString = "12 Nov 2011 12:00"
DateFormat formatter = new SimpleDateFormat("dd MMM yyyy HH:mm");
Date date = formatter.parse(dateString);
System.out.println(date.getDay());
this prints out day as 3 ? why is this happening ? how can I print the correct day?
Please read the documentation it is worth learning by that.
date.getDay() prints the day of the week. It should display 6 as that is a saturday, not sure how you got 3 as the result.
date.getDay() returns the day of the week as a zero-indexed numeric. In this case it should be Saturday (6).
Your result of Wednesday (3) suggests you are using a variation of the provided code and perhaps forgotten that the month is zero-indexed. e.g.
Date date = new Date(2011, 11, 12, 24, 0, 0); // month is now December, and time ticks over to Wednesday 13th
System.out.println(date.getDay()); // this would produce 3
I believe you want date.getDate().
If you are looking for a String representation of the day, take a look at this example:
http://www.java-examples.com/formatting-day-week-using-simpledateformat
Even better, check out the Joda-Time library, it is much more intuitive than the classes provided in the Java SDK. A future version of Java may even adopt a new date framework similar to Joda-Time (JSR-310)
The code you showed should print 6, as this date is a Saturday. On my computer that happens. without further information I cannot deduce more. Is this the complete code you execute? You could print the value of date.toString(), that would possibly give some more information.