Limit date range in Joda Time - java

I am using Joda Time library for parsing the string into dateTime using parseDateTime funtion in this library and noticed that date range supported for this library is -292,269,054 to 292,277,023.
Can anyone know on how to limit date range using this library.Especially with year (YYYY) to 9999?
Thanks.

Interval Class
You can limit date ranges in Joda-Time with Interval.
You can then query whether a DateTime is within that interval/range.

As MadProgrammer commented, limiting a date range is your job as the app developer. Joda-Time cannot know what you consider to be reasonable limits.
Bean Validation
To help with that chore of validating data, you might find the Bean Validation spec useful. Defined by JSR 303 (spec 1.0) and JSR 349 (spec 1.1).
With Bean Validation, you can conveniently use annotations to define rules such as a minimum and maximum value for a particular member variable in a class.

Joda-Time surprisingly offers what you want (really?). The apparent solution is using LimitChronology. An example:
DateTime min = new DateTime(2014, 1, 1, 0, 0);
DateTime max = new DateTime(2015, 1, 1, 0, 0).minusMillis(1);
Chronology chronology =
LimitChronology.getInstance(ISOChronology.getInstance(), min, max);
DateTime now = DateTime.now(chronology);
System.out.println(now.toString(DateTimeFormat.fullDateTime()));
// output: Donnerstag, 6. November 2014 19:08 Uhr MEZ
DateTime test = new DateTime(1970, 1, 1, 0, 0).withChronology(chronology);
System.out.println(test.toString(DateTimeFormat.fullDateTime()));
// no exception! => output: �, �. � ���� ��:�� Uhr MEZ
test = now.withYear(1970);
// IllegalArgumentException:
// The resulting instant is below the supported minimum of
// 2014-01-01T00:00:00.000+01:00 (ISOChronology[Europe/Berlin])
My advise is however not to use this feature.
First reason is the inconvenience to apply the LimitChronology on every DateTime-object in your program. Probably you would be forced to change your application architecture to install a central factory for producing such exotic DateTime-objects in order to be sure that you really don't forget any object.
Another reason is the partial unreliability of the chronology in question. It can not prevent instantiating DateTime-objects outside of the supported limited range but produces strange formatted output (see example above).
Therefore I suggest you to follow the advise of #MadProgrammer or #CharlieS using Interval for range checks.

I am not sure, but you can test with this code
DateTime startRange = new DateTime(2010, 1, 1, 12, 0, 0, 0);
DateTime endRange = new DateTime(9999, 12, 31, 21, 59, 59, 59);
Interval interval = new Interval(startRange, endRange);
DateTime testRange = new DateTime(2014, 10, 30, 11, 0, 0, 0);
System.out.println(interval.contains(testRange)); // returns true
endRange = new DateTime(2014, 12, 31, 21, 59, 59, 59);
testRange = new DateTime(9999, 10, 30, 11, 0, 0, 0);
System.out.println(interval.contains(testRange)); // returns false

Related

Find Latest timestamp from DateTime class

I have a list of DateTime objects and my task is to compare them and find the latest timestamp. The DateTime class is used from Joda API. I have been stuck on this part for a little while now. I would appreciate the help.
Collections.max()
DateTimeZone dtz = DateTimeZone.forID("Indian/Comoro");
List<DateTime> dateTimeObjects = Arrays.asList(
new DateTime(2021, 2, 13, 21, 0, dtz),
new DateTime(2021, 2, 13, 22, 0, dtz),
new DateTime(2021, 2, 13, 23, 0, dtz));
DateTime latestDateTime = Collections.max(dateTimeObjects);
System.out.println(latestDateTime);
Output:
2021-02-13T23:00:00.000+03:00
Collections.max() throws a NoSuchElementException if the list is empty. It throws a NullPointerException if the list contains a null (except perhaps if the null is the sole element, then it may get returned).
When comparing DateTime objects from different time zones Joda-Time first compares the instants, the points in time, so also in this case you are getting the latest. For example:
List<DateTime> dateTimeObjects = Arrays.asList(
new DateTime(2021, 2, 13, 21, 0, DateTimeZone.forID("Asia/Istanbul")),
new DateTime(2021, 2, 13, 22, 0, DateTimeZone.forID("Atlantic/Madeira")),
new DateTime(2021, 2, 13, 23, 0, DateTimeZone.forID("Antarctica/Macquarie")));
DateTime latestDateTime = Collections.max(dateTimeObjects);
System.out.println(latestDateTime);
Output:
2021-02-13T22:00:00.000Z
We got the time 22:00 for Madeira. You may wonder that the time 23:00 for Macquarie would seem later, but Macquarie is at UTC offset +11:00, so that time is in fact 10 hours earlier. DateTime objects are Comparable and compare by instant, not by clock hour. So we have got the latest point in time. We always will, also when several time zones are mixed.
Documentation link: Collections.max()

How to find a time between two dates

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

Subtracting milliseconds doesn't give expected result

Date day1 = set_datetime(2017, Calendar.JUNE, 28, 8, 00, 0);
Date day2 = set_datetime(2017, Calendar.JUNE, 28, 10, 00, 0);
def set_datetime(int year, int month, int day, int hour, int minute, int second) {
Calendar cal = Calendar.getInstance();
cal.set(year, month, day, hour, minute, second);
Date cal_date = cal.getTime();
return cal_date
}
println "\t\t (day2.getTime() - day1.getTime()) = " + (day2.getTime() - day1.getTime())
(day2.getTime() - day1.getTime()) = 7199996
Something seems wrong here, because when I convert the milliseconds to hours, 7199996 isn't exactly 2 hours (7200000). In fact, it's 4 milliseconds less. Why isn't it 7200000 milliseconds?
You're not clearing out the milliseconds field.
Add
cal.clear();
before the cal.set(...); line, or add
cal.set(Calendar.MILLISECONDS, 0);
More generally, don't use java.util.Calendar and java.util.Date: they are old, poorly-designed classes, fraught with well-known bugs.
Use classes from the java.time package, introduced in Java 8; or classes from threetenbp or JodaTime on earlier versions of Java.
In support of and as a modest supplement to the accepted answer, below is the ThreeTen-Backport version, which will work on Java 6.
LocalDateTime dateTime1 = LocalDateTime.of(2017, Month.JUNE, 28, 8, 0);
LocalDateTime dateTime2 = LocalDateTime.of(2017, Month.JUNE, 28, 10, 0);
System.out.println("\t\t (dateTime2 - dateTime1) = "
+ Duration.between(dateTime1, dateTime2).toMillis());
It prints:
(dateTime2 - dateTime1) = 7200000
Sorry I don’t have a Groovy/Grails environment running, so I have given you only Java code and will have to trust you to translate.
Your question is just one more about the outdated classes like Calendar that shows just one more example out of very many of how easily you get unexpected results from these and how little of a clue you’re often left with when this happens. Getting the ThreeTen-Backport for, in your case, Java 6 is certainly worth considering. In addition, the above code is also shorter, and in my opinion, clearer and more natural.

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 to create an iterater over days in JodaTime

I need to populate JComboBox with days as follows:
April 1, 2014
April 2, 2014
...
April 10,2014
I am using JodaTime to define dates. However, I don't know how to create an iterater over days in JodaTime.
JComboBox<String> days = new JComboBox<String>();
DateTime startD = new DateTime(2014, 4, 1, 0, 0, 0);
for (int i=0; i<10; i++)
{
// DateTime nextD = ...
days.addItem(startD.toString(DateTimeFormat.forPattern("yyyyMMdd")));
}
DateTime currentDate = startD.plusDays(i);
You should have found that easily by reading the javadoc.
Note that unless you really want the items to represent a precise instant (i.e. the first april at midnight in your timezone), you should probably use a LocalDate instead of a DateTime.

Categories