How to find a time between two dates - java

How can I check if specific time will occur between two dates, for example:
time -> 11:34
dates 1.12 17:00 <-> 2.12 17:01

LocalDateTime startDateTime = LocalDateTime.of(2017, Month.DECEMBER, 1, 17, 0);
LocalDateTime endDateTime = LocalDateTime.of(2017, Month.DECEMBER, 2, 17, 1);
LocalTime timeToTest = LocalTime.of(11, 34);
// Does the timeToTest occur some time between startDateTime and endDateTime?
LocalDateTime candidateDateTime = startDateTime.with(timeToTest);
if (candidateDateTime.isBefore(startDateTime)) {
// too early; try next day
candidateDateTime = candidateDateTime.plusDays(1);
}
if (candidateDateTime.isAfter(endDateTime)) {
System.out.println("No, " + timeToTest + " does not occur between " + startDateTime + " and " + endDateTime);
} else {
System.out.println("Yes, the time occurs at " + candidateDateTime);
}
This prints
Yes, the time occurs at 2017-12-02T11:34
It’s a little bit tricky. I am exploiting the fact that LocalTime implements the TemporalAdjuster interface, which allows me to adjust one into another date-time class, in this case startDateTime. I don’t know at first whether this will adjust the time forward or backward, so I need to test that in a subsequent if statement.
Please consider whether you wanted your date-time interval to be inclusive/closed, exclusive/open or half-open. The standard recommendation is the last: include the start time, exclude the end time; but only you know your own requirements.
Also be aware that using LocalDateTime prevents taking summer time (DST) and other transitions into account. For example, if moving the clock forward in spring, some times of day will not exist that day, but the above code will be happy to tell you they do exist.

The idea would be calculating the dates between start and end date. Then pair it with your specific time and check if any date time matches the following constraint: start <= date + time <= end.
public boolean isTimeInBetween(LocalDateTime start, LocalDateTime end, LocalTime time) {
return start.toLocalDate().datesUntil(end.plusDays(1).toLocalDate())
.anyMatch(d -> !(d.atTime(time).isBefore(start) || d.atTime(time).isAfter(end)));
}

You can define 3 variables, start, end and a test time. Using Java 8's LocaleDateTime makes this simple enough. See example below with 3 test cases:
public static void main(String[] args) {
LocalDateTime start = LocalDateTime.of(2017, 12, 1, 17, 0);
LocalDateTime end = LocalDateTime.of(2017, 12, 2, 17, 1);
System.out.println("Test with time before range");
System.out.println(isInRange(start, end, LocalDateTime.of(2017, 12, 1, 12, 0)));
System.out.println("Test with time in range");
System.out.println(isInRange(start, end, LocalDateTime.of(2017, 12, 2, 11, 34)));
System.out.println("Test with time after range");
System.out.println(isInRange(start, end, LocalDateTime.of(2017, 12, 2, 20, 0)));
}
private static boolean isInRange(LocalDateTime start, LocalDateTime end, LocalDateTime test) {
return !(test.isBefore(start) || test.isAfter(end));
}
Output:
Test with time before range
false
Test with time in range
true
Test with time after range
false

Related

java timestamps calculate overlap duration with interval

I have many timestamps (start, end) which define an interval and want to efficiently check if they overlap another single interval. If yes, compute overlap duration, otherwise return 0.
interval: 18:00 same day until 08:00 the next day.
start | end
2018-01-02 14:59:18.922|2018-01-02 14:59:38.804
2018-01-02 18:32:59.348|2018-01-02 20:30:41.192
2018-01-02 01:54:59.363|2018-01-02 01:54:59.363
2018-01-03 00:10:38.831|2018-01-03 00:11:53.103
I am unsure how to efficiently define the next day efficiently.
edit
LocalDate
has a method toInterval().overlaps(anotherInterval). I simply am unsure how to get fitting interval (18:00 - 08:00 next day) in a generic way, i.e. without manually reading the YYYMMDD and then creating a new object.
a bit similar is Find if hours ranges overlap regardless of the date
edit 2
toInterval is only present for jodatime - not java.time / JSR-310. What would be a viable way to calculate overlap duration with java.time?
edit3
A solution with jodaTime:
val begin = new DateTime(new java.sql.Timestamp().getTime())
val stop = new DateTime(new java.sql.Timestamp().getTime())
val i1 = new Interval(begin, stop)
val start = new DateTime(begin.year.get , begin.monthOfYear.get, begin.dayOfMonth.get, startHour, 0, 0, 0);
val endIntermediate =stop.toDateTime.plusDays(1)
val end = new DateTime(endIntermediate.year.get , endIntermediate.monthOfYear.get, endIntermediate.dayOfMonth.get, endHour, 0, 0, 0);
val i2 = new Interval(start, end)
val overlap = i1.overlap(i2)
val overlapDurationOrNull = overlap.toDuration
seems to work, but still is clumsy.
I believe that the following method gives you the equivalent of your Joda-Time solution.
private static final LocalTime START = LocalTime.of(18, 0);
private static final LocalTime END = LocalTime.of(8, 0);
public static Duration overlap(ZonedDateTime currentStart, ZonedDateTime currentEnd) {
ZonedDateTime singleIntervalStart = currentStart.with(START);
ZonedDateTime singleIntervalEnd = currentStart.plusDays(1).with(END);
if (currentEnd.isBefore(singleIntervalStart)) {
// no overlap
return Duration.ZERO;
}
ZonedDateTime overlapStart = currentStart.isBefore(singleIntervalStart)
? singleIntervalStart : currentStart;
ZonedDateTime overlapEnd = currentEnd.isBefore(singleIntervalEnd)
? currentEnd : singleIntervalEnd;
return Duration.between(overlapStart, overlapEnd);
}
For trying it out with the timestamps from your question I am using the following utility method:
private static void demo(String from, String to) {
ZoneId zone = ZoneId.of("Atlantic/Stanley");
Duration overlapDuration = overlap(LocalDateTime.parse(from).atZone(zone),
LocalDateTime.parse(to).atZone(zone));
System.out.println("" + from + " - " + to + ": " + overlapDuration);
}
Now I call it like this:
demo("2018-01-02T14:59:18.922", "2018-01-02T14:59:38.804");
demo("2018-01-02T18:32:59.348", "2018-01-02T20:30:41.192");
demo("2018-01-02T01:54:59.363", "2018-01-02T01:54:59.363");
demo("2018-01-03T00:10:38.831", "2018-01-03T00:11:53.103");
And the output is:
2018-01-02T14:59:18.922 - 2018-01-02T14:59:38.804: PT0S
2018-01-02T18:32:59.348 - 2018-01-02T20:30:41.192: PT1H57M41.844S
2018-01-02T01:54:59.363 - 2018-01-02T01:54:59.363: PT0S
2018-01-03T00:10:38.831 - 2018-01-03T00:11:53.103: PT0S
In the first example 14:59 is before 18:00, so the result is an overlap of 0. In the second example the whole interval is counted as overlap (nearly 2 hours). Note that in the last two examples no overlap is reported because the the times are many hours before 18:00. I am unsure whether this is what you wanted since the times are also before 08:00.
You can simply use LocalDate.plusDays to add a day.
Assuming an iteration where the following are to be compared:
LocalDateTime d1 = LocalDateTime.parse("2018-01-02T14:59:18"),
d2 = LocalDateTime.parse("2018-01-02T14:59:38");
You can create the 18:00 and 08:00 date/time objects using:
LocalDateTime start = LocalDateTime.of(d1.toLocalDate(), LocalTime.of(18, 0));
LocalDateTime end = LocalDateTime.of(d1.toLocalDate().plusDays(1),
LocalTime.of(8, 0));
I've assumed that 18:00 is on the same day as d1.

How to create calender event using java date and time?

I want to create time events in Java.
For example: between March 1st to March 31st I want to create some events with start and end dates like:
Event1: March 3 to March 5
Event2: March 6 to March 10
Event2: March 13 to March 25
and so on.
These events should not overlap and an event should not contain March 1st and March 31st.
How I can do it using Java date and Time or Joda-Time?
I have an only basic idea that create a class with four variables as follows:
monthStart
monthEnd
eventStart
eventEnd
One alternative is to use the Time4J API, and create a DateInterval using PlainDate as start and end dates:
// March 1st and 31st
PlainDate start = PlainDate.of(2018, 3, 1);
PlainDate end = PlainDate.of(2018, 3, 31);
DateInterval interval = DateInterval.between(start, end);
With this, you can check if an event is inside this interval:
// Event1: March 3 to March 5
PlainDate eventStart = PlainDate.of(2018, 3, 3);
PlainDate eventEnd = PlainDate.of(2018, 3, 5);
DateInterval event1 = DateInterval.between(eventStart, eventEnd);
if (interval.encloses(event1)) {
// event1 is inside interval
}
You can also check if 2 events overlap:
// Event2: March 3 to March 5
eventStart = PlainDate.of(2018, 3, 6);
eventEnd = PlainDate.of(2018, 3, 10);
DateInterval event2 = DateInterval.between(eventStart, eventEnd);
if (event1.overlaps(event2)) {
// events 1 and 2 overlap
}
Your algorithm would be: create the full interval (such as from March 1st to March 31st) and create your events intervals, and then use the methods above (encloses and overlaps) accordingly.
Plain Java
With just Java's API, assuming you have Java 8, it's similar. The only difference is that Java doesn't have an Interval class and you have to compare the dates manually:
// March 1st and 31st
LocalDate start = LocalDate.of(2018, 3, 1);
LocalDate end = LocalDate.of(2018, 3, 31);
// Event1: March 3 to March 5
LocalDate eventStart = LocalDate.of(2018, 3, 3);
LocalDate eventEnd = LocalDate.of(2018, 3, 5);
// check if event1 is inside start and end dates
if (start.isBefore(eventStart) && eventEnd.isBefore(end)) {
// event is inside March 1st and 31st
}
LocalDate has the methods isBefore and isAfter to check if another date is before or after the date, and it also has the equals method to know if 2 dates are the same. The logic to know if 2 events overlap can be achieved by only using those methods as well, and "it's left as an exercise to the reader" :-)
If you have Java 7 or below, you can use the threeten backport, which has the LocalDate class as well.
Threeten Extra
In this API, there's an Interval class, but it works only with Instant, not with LocalDate.
You can make some workaround on this and assume that your dates are in UTC, and then use the Interval. I created an auxiliary method to do such conversion:
public Instant toInstant(LocalDate date) {
// convert to midnight in UTC
return date.atStartOfDay(ZoneOffset.UTC).toInstant();
}
And then use this to create the intervals:
// March 1st and 31st
Instant start = toInstant(LocalDate.of(2018, 3, 1));
Instant end = toInstant(LocalDate.of(2018, 3, 31));
Interval interval = Interval.of(start, end);
// Event1: March 3 to March 5
Instant eventStart = toInstant(LocalDate.of(2018, 3, 3));
Instant eventEnd = toInstant(LocalDate.of(2018, 3, 5));
Interval event1 = Interval.of(eventStart, eventEnd);
if (interval.encloses(event1)) {
// event1 is inside interval
}
// Event2: March 3 to March 5
eventStart = toInstant(LocalDate.of(2018, 3, 6));
eventEnd = toInstant(LocalDate.of(2018, 3, 10));
Interval event2 = Interval.of(eventStart, eventEnd);
if (event1.overlaps(event2)) {
// events 1 and 2 overlap
}
This is a workaround because it artificially sets the dates to midnight in UTC. As we only care about the day, month and year, though, this should do the trick (or you can also download the threeten extra's code and create another Interval class that works with LocalDate, and base your code on the original).
Joda-Time
Joda-Time is a discontinued project and the team is advising the migration to java.time API. Check in Joda-Time's website, there's a warning there saying:
Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310)
If you use Java 6 or 7, I recommend the threeten backport. For Java >= 8, use java.time with threeten extra or time4j. Only if you still use Java 5, then the best alternative is to use Joda-Time (actually, the ideal is to upgrade to a newer Java version, but anyway).
In Joda-Time, there are similar classes: LocalDate and Interval, and you need to convert the LocalDate to DateTime (assuming midnight in UTC) in order to work with intervals (similar to the conversion to Instant that we made above):
DateTime start = new LocalDate(2018, 3, 1).toDateTimeAtStartOfDay(DateTimeZone.UTC);
DateTime end = new LocalDate(2018, 3, 31).toDateTimeAtStartOfDay(DateTimeZone.UTC);
Interval interval = new Interval(start, end);
DateTime eventStart = new LocalDate(2018, 3, 3).toDateTimeAtStartOfDay(DateTimeZone.UTC);
DateTime eventEnd = new LocalDate(2018, 3, 5).toDateTimeAtStartOfDay(DateTimeZone.UTC);
Interval event1 = new Interval(eventStart, eventEnd);
// contains accepts the same start or end dates, it needs additional checks to make sure dates are different
if (interval.contains(event1)
&& event1.getStart().isAfter(interval.getStart())
&& event1.getEnd().isBefore(interval.getEnd())) {
// interval contains event1, and start and end dates are not the same
}
eventStart = new LocalDate(2018, 3, 6).toDateTimeAtStartOfDay(DateTimeZone.UTC);
eventEnd = new LocalDate(2018, 3, 10).toDateTimeAtStartOfDay(DateTimeZone.UTC);
Interval event2 = new Interval(eventStart, eventEnd);
if (event1.overlaps(event2)) {
// events 1 and 2 overlap
}

Java Vs C# Long to DateTime Conversion

In Java I have the following test that passes fine
// 42 bits of time is good enough for the next 100 years.
// An IEEE double has 52 bits of mantissa, so our dates can be easily fit.
#Test
public void testMaxBits() throws ParseException {
// Maximum 42 bit integer
long millis = (1L << 42) - 1;
Date date = new Date(millis);
//DateTime maxDate = new DateTime(2109, 5, 15, 8, 35, 11, 103);
Date maxDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2109-05-15T08:35:11.103");
Assert.assertEquals(maxDate, date);
}
Now, I want to do the same sort of thing in C#, so I have a test in LinqPAD that test the C# implementation for correctness
DateTime maxDate = new DateTime(2109, 5, 15, 8, 35, 11, 103);
long beginTicks = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
long l = (1L << 42) - 1;
DateTime date = new DateTime(beginTicks + l, DateTimeKind.Utc);
maxDate.Dump();
date.Dump();
The output don't match, the values outputted ToString() values are
maxDate = 15/05/2109 08:35:11
date = 06/01/1970 02:10:04
What am I missing here?
Edit. I have see a great answer below from #zmitrok, I have changed
DateTime date = new DateTime(beginTicks + l, DateTimeKind.Utc);
to
DateTime date = new DateTime(beginTicks +
l * TimeSpan.TicksPerMillisecond, DateTimeKind.Utc);
but now get
date = 15/05/2109 07:35:11
Where has the hour gone?
Your test is basically confusing ticks with milliseconds. If you only need to store a number of milliseconds since the unix epoch, then do so - but I'd recommend using something like this to perform the conversion:
public static readonly DateTime UnixEpoch
= new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public DateTime FromMillisecondsSinceUnixEpoch(long milliseconds)
{
return UnixEpoch.AddMilliseconds(milliseconds);
}
(As a side-note, that method already exists in my Noda Time project... hint hint :)
Your test would then be:
[TestMethod]
public void TestMaxBits()
{
long maxMillis = (1L << 42) - 1;
DateTime maxDate = DateTimeHelper.FromMillisecondsSinceUnixEpoch(maxMillis);
Assert.Greater(maxDate, new DateTime(2100, 1, 1, 0, 0, 0));
}
Note that:
This code doesn't mention ticks at all, because you're not interested in ticks
This code doesn't assert that the maximum date is some very specific value, because that's not what you care about; you care that 42 bits of time will carry you until the end of the century. (The "next 100 years" comment is somewhat specious, as 2109 is less than 100 years away from now, so I'll assume it really means "until the end of the 21st century.")
That of course make your question of "Where has the hour gone?" irrelevant - but the answer to that is simply that SimpleDateFormat defaults to using the system time zone, so you're actually relying on the time zone of the system you're running the test on, which is a really bad idea. If you set the time zone of the SimpleDateFormat to UTC, you'll find that it's 07:35:11 in Java as well.
The constructor you are using takes ticks as the first argument, however you are passing a value that was added to milliseconds.
Ticks: A date and time expressed in the number of 100-nanosecond intervals that have elapsed since January 1, 0001 at 00:00:00.000 in the Gregorian calendar.
I think you need to multiply ticks by this constant: https://msdn.microsoft.com/en-us/library/system.timespan.tickspermillisecond%28v=vs.110%29.aspx

How can I check that a two dates are between a period(interval)?

My input is a beginning date and an end date. And I want to check that it is between December 1 and March 31. (The year can change, and there will be only dates in, or dates outside this period).
So far I didn't found any solution with Joda-time. Can somebody give me a starting point how to do(not code, just the logic)?
I didn't checked the code yet, but it is VERY Ugly, and I want to find an algorithmic solution
public static boolean isInWinter(Contract contract) {
logger.info("begin isInWinter:");
DateTime beginDate = contract.getBeginningDate();
DateTime endDate = contract.getEndDate();
/*
* If the year is different (etc 2012 dec,2013 marc) check that the
* beginning month is december, and the end month is jan,feb,marc
*/
if (endDate.getYear() - beginDate.getYear() == 1) {
if ((beginDate.getMonthOfYear() == 12)
&& ((endDate.getMonthOfYear() == 1
|| endDate.getMonthOfYear() == 2 || endDate
.getMonthOfYear() == 3))) {
logger.info("return true different year");
return true;
}
/*
* Same year can be if begin and end date is december or begin and
* and date is jan,febr,marc TODO REMOVE Auto formatter
*/
} else if (endDate.getYear() - beginDate.getYear() == 0) {
if ((beginDate.getMonthOfYear() == 12 && endDate.getMonthOfYear() == 12)
|| ((beginDate.getMonthOfYear() == 1
|| beginDate.getMonthOfYear() == 2 || beginDate
.getMonthOfYear() == 3) && (endDate
.getMonthOfYear() == 1
|| endDate.getMonthOfYear() == 2 || endDate
.getMonthOfYear() == 3))) {
logger.info("return true same year");
return true;
}
}
logger.info("return false");
return false;
}
Joda-Time
Joda-Time and its Interval class with the overlap method is just what you need. Very easy.
Time Zone
Unlike java.util.Date, a DateTime truly knows its assigned time zone. Generally better to specify than rely on default. Note that if you are tracking date+time (a DateTime rather than a LocalDate), then time zone is critical. The definition of a "day" beginning and ending depends on time zone. Businesses that work across time zones may choose to use UTC for this purpose.
Span of Time
FYI, besides Interval, Joda-Time also offers the Period and Duration classes for working with a span of time.
Start of Day
When working with date-time values but focusing on a "day", then adjust the time portion to the first moment of the day for that particular time zone. In Joda-Time, simply call the withTimeAtStartOfDay method.
Avoid the "midnight"-related classes and methods. They are no longer recommended by the Joda-Time team.
Half-Open
Joda-Time uses the "Half-Open" approach when comparing spans of time. The beginning in inclusive and the ending is exclusive. That means since you want from December 1 to March 31 that we define an Interval of the first moment of the day of December 1 to the first moment of the day of April 1. If you ponder this, and search StackOverflow for other postings on "joda interval", you'll come to see the wisdom of this approach.
Comparison
The Interval class offers handy comparison methods: contains, abuts, overlap, and gap. Be sure to read the doc to understand exact details.
java.time
Java 8 brings the new java.time package, inspired by Joda-Time, defined by JSR 310. Joda-Time continues to work in Java 8, but you may want to learn java.time rather than Joda-Time if you are new to date-time work.
Example Code
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Budapest" ); // Or, perhaps DateTimeZone.UTC
// Inputs
DateTime contractStart = new DateTime( 2014, 1, 2, 0, 0, 0, timeZone );
DateTime contractStop = new DateTime( 2014, 5, 6, 0, 0, 0, timeZone );
Interval contractInterval = new Interval( contractStart, contractStop );
// Target to test
DateTime targetStart = new DateTime( 2013, DateTimeConstants.DECEMBER, 1, 0, 0, 0, timeZone );
DateTime targetStop = targetStart.plusMonths( 4 );
Interval targetInterval = new Interval( targetStart, targetStop );
boolean targetContainsContract = targetInterval.contains( contractInterval );
boolean targetOverlapsContract = ( targetInterval.overlap( contractInterval ) != null );
Dump to console…
System.out.println( "contractInterval: " + contractInterval );
System.out.println( "targetInterval: " + targetInterval );
System.out.println( "targetContainsContract: " + targetContainsContract );
System.out.println( "targetOverlapsContract: " + targetOverlapsContract );
When run…
contractInterval: 2014-01-02T00:00:00.000+01:00/2014-05-06T00:00:00.000+02:00
targetInterval: 2013-12-01T00:00:00.000+01:00/2014-04-01T00:00:00.000+02:00
targetContainsContract: false
targetOverlapsContract: true
Create an interval with the beginning and end date you get as inputs.
Create an interval going from March to December.
Your condition is true if either:
the march to december interval entirely contains the input interval,
or there is no overlap between the two intervals.
In code that would be:
marchToDecember.contains(interval) || marchToDecember.overlap(interval) == null
Javadàoc for Interval
Since you mention that the dates will be either both in or both out the period, you'll only need to test one of them.
So just test whether beginDate.getMonthOfYear() returns either 12, 1, 2 or 3.

Y returns 2012 while y returns 2011 in SimpleDateFormat

I wonder why 'Y' returns 2012 while 'y' returns 2011 in SimpleDateFormat:
System.out.println(new SimpleDateFormat("Y").format(new Date())); // prints 2012
System.out.println(new SimpleDateFormat("y").format(new Date())); // prints 2011
Can any one explain why?
week year and year. From javadoc
A week year is in sync with a WEEK_OF_YEAR cycle. All weeks between
the first and last weeks (inclusive) have the same week year value.
Therefore, the first and last days of a week year may have different
calendar year values.
For example, January 1, 1998 is a Thursday. If getFirstDayOfWeek() is
MONDAY and getMinimalDaysInFirstWeek() is 4 (ISO 8601 standard
compatible setting), then week 1 of 1998 starts on December 29, 1997,
and ends on January 4, 1998. The week year is 1998 for the last three
days of calendar year 1997. If, however, getFirstDayOfWeek() is
SUNDAY, then week 1 of 1998 starts on January 4, 1998, and ends on
January 10, 1998; the first three days of 1998 then are part of week
53 of 1997 and their week year is 1997.
Here's a Java 8 update with some code, as GregorianCalendar will probably be deprecated or removed from future JDK versions.
The new code is handled in the WeekFields class, and specifically for the lower case y / upper case Y with the weekBasedYear() field accessor.
Returns a field to access the year of a week-based-year based on this
WeekFields. This represents the concept of the year where weeks start
on a fixed day-of-week, such as Monday and each week belongs to
exactly one year. This field is typically used with dayOfWeek() and
weekOfWeekBasedYear().
Week one(1) is the week starting on the getFirstDayOfWeek() where
there are at least getMinimalDaysInFirstWeek() days in the year. Thus,
week one may start before the start of the year. If the first week
starts after the start of the year then the period before is in the
last week of the previous year.
This field can be used with any calendar system.
In the resolving phase of parsing, a date can be created from a
week-based-year, week-of-year and day-of-week.
In strict mode, all three fields are validated against their range of
valid values. The week-of-year field is validated to ensure that the
resulting week-based-year is the week-based-year requested.
In smart mode, all three fields are validated against their range of
valid values. The week-of-week-based-year field is validated from 1 to
53, meaning that the resulting date can be in the following
week-based-year to that specified.
In lenient mode, the year and day-of-week are validated against the
range of valid values. The resulting date is calculated equivalent to
the following three stage approach. First, create a date on the first
day of the first week in the requested week-based-year. Then take the
week-of-week-based-year, subtract one, and add the amount in weeks to
the date. Finally, adjust to the correct day-of-week within the
localized week.
The setup of this WeekFields instance depends on the locale and may have different settings depending on it, US and European countries like France may have a different day as start of the week.
For example the DateFormatterBuilder of Java 8, instantiate the parser with the locale, and use this locale for the Y symbol :
public final class DateTimeFormatterBuilder {
...
private void parsePattern(String pattern) {
...
} else if (cur == 'Y') {
// Fields defined by Locale
appendInternal(new WeekBasedFieldPrinterParser(cur, count));
} else {
...
static final class WeekBasedFieldPrinterParser implements DateTimePrinterParser {
...
/**
* Gets the printerParser to use based on the field and the locale.
*
* #param locale the locale to use, not null
* #return the formatter, not null
* #throws IllegalArgumentException if the formatter cannot be found
*/
private DateTimePrinterParser printerParser(Locale locale) {
WeekFields weekDef = WeekFields.of(locale);
TemporalField field = null;
switch (chr) {
case 'Y':
field = weekDef.weekBasedYear();
if (count == 2) {
return new ReducedPrinterParser(field, 2, 2, 0, ReducedPrinterParser.BASE_DATE, 0);
} else {
return new NumberPrinterParser(field, count, 19,
(count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD, -1);
}
case 'e':
case 'c':
field = weekDef.dayOfWeek();
break;
case 'w':
field = weekDef.weekOfWeekBasedYear();
break;
case 'W':
field = weekDef.weekOfMonth();
break;
default:
throw new IllegalStateException("unreachable");
}
return new NumberPrinterParser(field, (count == 2 ? 2 : 1), 2, SignStyle.NOT_NEGATIVE);
}
...
}
...
}
Here's some example
System.out.format("Conundrum : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
.format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'")));
System.out.format("Solution : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmms'S'")));
System.out.format("JVM Locale first day of week : %s%n",
WeekFields.of(Locale.getDefault()).getFirstDayOfWeek());
System.out.format("US first day of week : %s%n",
WeekFields.of(Locale.US).getFirstDayOfWeek());
System.out.format("France first day of week : %s%n",
WeekFields.of(Locale.FRANCE).getFirstDayOfWeek());
System.out.format("JVM Locale min days in 1st week : %s%n",
WeekFields.of(Locale.getDefault()).getMinimalDaysInFirstWeek());
System.out.format("US min days in 1st week : %s%n",
WeekFields.of(Locale.US).getMinimalDaysInFirstWeek());
System.out.format("JVM Locale min days in 1st week : %s%n",
WeekFields.of(Locale.FRANCE).getMinimalDaysInFirstWeek());
System.out.format("JVM Locale week based year (big Y): %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
System.out.format("France week based year (big Y) : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.FRANCE).weekBasedYear()));
System.out.format("US week based year (big Y) : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC")).get(WeekFields.of(Locale.US).weekBasedYear()));
And in regard of the locale and the upper case Y, you can either play with the command line option -Duser.language= (fr, en, es, etc.), or force the locale at invocation time :
System.out.format("English localized : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
.format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.ENGLISH)));
System.out.format("French localized : %s%n",
ZonedDateTime.of(2015, 12, 30, 0, 0, 0, 0, ZoneId.of("UTC"))
.format(DateTimeFormatter.ofPattern("YYYYMMdd'T'HHmms'S'", Locale.FRENCH)));
Format Y to get week year if calendar support week year. (getCalendar().isWeekDateSupported())
I learned the hard way the JSTL tag library format:date with short as the requested format uses YYYY under the covers. Which can indeed roll the printed date ahead a year.
I convert a date back and forth - you would expect the same year when you do this.
Notice how it advances one!
This is bad: YYYY!
You can run it here.
import java.util.Date;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import static java.lang.System.out;
class Playground {
public static Date convertYYYYMMDDStr(String s) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date result = null;
try {
result = sdf.parse(s);
} catch(ParseException e) {
e.printStackTrace();
}
return result;
}
public static String formatDateToStrWithSDF(Date d, SimpleDateFormat s) {
return s.format(d);
}
public static void main(String[ ] args) {
// DON'T DO. Use yyyy instead of YYYY
SimpleDateFormat sdfdmy = new SimpleDateFormat("dd-MM-YYYY");
String jan1st2020sb = "2020-01-01";
Date jan1st2020d = convertYYYYMMDDStr(jan1st2020sb);
String jan1st2020sa = formatDateToStrWithSDF(jan1st2020d, sdfdmy);
out.println(jan1st2020sb);
out.println(jan1st2020d);
out.println(jan1st2020sa);
String dec31st2020sb = "2020-12-31";
Date dec31st2020d = convertYYYYMMDDStr(dec31st2020sb);
String dec31st2020sa = formatDateToStrWithSDF(dec31st2020d, sdfdmy);
out.println(dec31st2020sb);
out.println(dec31st2020d);
out.println(dec31st2020sa);
}
}
This is good: yyyy

Categories