Hamcrest Date Matchers - java

I need to test before/after on dates in a certain test case. I'd like to use Hamcrest matchers if possible.
Are there any matchers for Hamcrest (Java) for working with Dates? If so, what package/class would I find the particular date matcher functions in?

The OrderingComparison::greaterThan matcher will work on any type which is comparable to itself (it's in the org.hamcrest.number package, but it's not actually number-specific). Date is such a type.

There is a library of hamcrest date matchers provided by the library at https://github.com/eXparity/hamcrest-date which is also available for maven, ivy, etc at
<dependency>
<groupId>org.exparity</groupId>
<artifactId>hamcrest-date</artifactId>
<version>1.1.0</version>
</dependency>
It supports various matchers for dates so allows constructs such as
Date myBirthday = new Date();
MatcherAssert.assertThat(myBirthday, DateMatchers.after(Moments.today()));
or
Date myBirthday = new Date();
MatcherAssert.assertThat(myBirthday, DateMatchers.isToday());

You can have a look at the new Date Matchers that will be added to hamcrest (I don't know when thought):
Date matchers discussion/code changes on github
After a quick look it seems there will be a new package org.hamcrest.date containing:
IsAfter
IsBefore
IsSameDayOfTheMonth
IsSameDayOfTheWeek
IsSameDayOfTheYear
IsSameHour
IsSameInstant
IsSameMinute
IsSameMonth
IsSameSecond
IsSameYear
IsWithin

There are certain hamcrest extensions that can ease some of the testing related to dates. Please check here.

The Matchers#greaterThan matcher works with Dates and other Comparable objects.
Here's the way to check that your date is greater than or equal (≥) to some expected date:
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.AnyOf.anyOf;
...
Date expectedMin = new Date()
// Execute the method being tested
Date resultDate = getDate();
// Validate
assertThat(resultDate, anyOf(greaterThan(expectedMin), equalTo(expectedMin)))

There is also the Cirneco extension. It has several Date specific matchers (e.g. monday()) and others that apply to dates because of the implementation of Comparable (see for instance between(), betweenInclusive()). The plan is to support also Joda Time in the JDK7 version of the library and the new date-based classes in the JDK8 version (mainly LocalDate).
You can do assertions like:
final Date date = new Date();
assertThat(date, is(monday())); // JUnit style
given(date).assertIs(monday()); // Cirneco style
You can use the following dependency for a JDK7-compliant project:
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>java7-hamcrest-matchers</artifactId>
<version>0.7.0</version>
</dependency>
or the following if you are using JDK8
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>java8-hamcrest-matchers</artifactId>
<version>0.7.0</version>
</dependency>

https://assertj.github.io/doc/#assertj-core-recursive-comparison
org.assertj:assertj-core:3.12.2
assertThat(actual)
.usingRecursiveComparison()
.ignoringFieldsMatchingRegexes("fieldToIgore")
.isEqualTo(expected);

Related

Alternative to org.easymock.EasyMock.capture in Java 8 to 11

I'm trying to capture values in my test
...
final Capture<MyDTO> myDTOCaptured = new Capture<MyDTO>();
EasyMock.expect(this.serviceMock.execute(capture(myDTOCaptured)).andReturn(someResult);
...
The Documentation says:
Deprecated API
org.easymock.EasyMock.capture(Capture)
Because of harder erasure enforcement, doesn't compile in Java 7
http://easymock.sourceforge.net/api/easymock/3.1/deprecated-list.html
Version 3.1 is the version we are using.
Is there any alternative for capture in Java 8+? I guess the alternative is upgrading the version.
Upgrade to latest EasyMock (4.2) and use generic method capture(Capture<T> captured) as a replacement
Expect any object but captures it for later use
Example:
final Capture<TwoWayChannelMessage> initiateCapture = new Capture<>();
connection.sendToClient(capture(initiateCapture));
This website is obsolete. The real up-to-date EasyMock website is http://easymock.org/. EasyMock moved out of SourceForge a long time ago.
Latest EasyMock would be
Capture<TwoWayChannelMessage> initiateCapture = EasyMock.newCapture();
connection.sendToClient(capture(initiateCapture));

How to change the calendar to Gregorian in Thailand locale

In my Java 8u51 application, I need to use Thailand locale.
However, I would like to use Gregorian Calendar instead of Buddhist Calendar.
I tried to replace java.util.Calendar 's CalendarProvider with SPI, but it has not worked out.
import java.security.AccessController;
...
import sun.util.locale.provider.LocaleProviderAdapter;
import sun.util.spi.CalendarProvider;
...
public static void main(String[] args) {
try {
System.out.println(AccessController.doPrivileged(new sun.security.action.GetPropertyAction("java.locale.providers")));
Locale l = Locale.getDefault(Locale.Category.FORMAT);
CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, l).getCalendarProvider();
provider.getInstance(TimeZone.getDefault(), l);
LocaleProviderAdapter.getAdapterPreference().forEach(o -> System.out.printf("Adapter: %s%n", o));
System.out.printf("Provider: %s%n", provider.getClass());
System.out.printf("Availables: %s%n", Calendar.getAvailableCalendarTypes());
System.out.printf("Calendar: %s%n", Calendar.getInstance().getClass());
}
...
src/META-INF/services/sun.util.spi.CalendarProvider is as follows.
sun.util.locale.provider.AlwaysGregorianCalendarProviderImpl
Omit the source of AlwaysGregorianCalendarProviderImpl.
result,
$ java -jar Sample.jar -Djava.locale.providers=SPI -Duser.language=th -Duser.country=TH
SPI
Adapter: SPI
Adapter: FALLBACK
Provider: class sun.util.locale.provider.CalendarProviderImpl
Availables: [gregory, buddhist, japanese]
Calendar: class sun.util.BuddhistCalendar
Buddhist Calendar will be used.
How can I change it to a Gregorian calendar?
Self reply late.
The SPI implementation by Sun did not allow external expansion. It also checks the filenames of the divided inner classes.
Although I did not investigate in detail, it seems that it can not cope even with CLDRLocaleProviderAdapter.

Bug in Finnish date localization?

I'm trying to localize for Finland using this code:
Locale finLocale = new Locale("fi", "FI");
Date today = new Date(2017, 1, 1);
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.LONG, finLocale);
String formattedDate = dateFormat.format(today);
System.out.println(formattedDate);
What I end up getting is "helmikuutata". I would expect "helmikuu" or "helmikuuta", but this just seems wrong.
Is this valid Finnish, or is there a bug in Java? My version is 1.8.0_31
Yes, this was a bug in JDK (See JDK-8074791), wherein an extra 'ta' was appended to the month name. This got fixed from JDK 8u60 version onwards. So, if you upgrade to latest JDK versions like JDK8u131, you will get the correct output.
I am convinced that the answer by Pallavi Sonal is correct. I have already upvoted it and you should probably accept it. I had wanted to keep the following a comment, but it deserves better formatting, so here goes.
java.time
Since you are using Java 8 (and even if you didn’t), you will prefer the modern more programmer friendly API of java.time:
LocalDate today = LocalDate.of(2017, Month.FEBRUARY, 1);
DateTimeFormatter dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG)
.withLocale(finLocale);
String formattedDate = today.format(dateFormat);
On my Java 1.8.0_131 it gives the expected
1. helmikuuta 2017
If someone reading this is using Java 6 or 7, please consider getting the ThreeTen Backport library so you can use the modern date and time API as shown.

DateTimeFormatter pattern with liternal and no separator does not work

The parser generated by DateTimeFormatter.ofPattern exhibits the following interesting behaviour which is preventing me from writing a pattern to parse a string like 20150100:
System.out.println(DateTimeFormatter.ofPattern("yyyyMM").parse("201501", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'aa'").parse("201501aa", YearMonth::from)); // works
System.out.println(DateTimeFormatter.ofPattern("yyyyMM'00'").parse("20150100", YearMonth::from));
// java.time.format.DateTimeParseException: Text '20150100' could not be parsed at index 0
I debuged the code, it seems the problem is caused by the year field parsing beyond the end of the string (max width for three y's and more is always 19). However, I don't understand how it could work for the pattern without the '00' literal at the end.
Is there any way to fix this withing having to use a formatter builder?
Edit:
Since Jarrod below confirmed it's buggy, I did more googling and finally found the bug reports:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8031085
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8032491
Both are only fixed in Java 9 though......
There is a bug in the DateTimePrinterParser:
I step debugged all the way through it, apparently you can not have digits as literals. Similar test codes proves this if you step debug all the way through to the DateTimeFormatterBuilder.parse() method you can see what it is doing wrong.
Apparently the Value(YearOfEra,4,19,EXCEEDS_PAD) parser consumes the 00 where they stop if those are not digits because it is looking for a number 4 to 19 digits long. The DateTimeFormatter that is embedded in the DateTimeParseContext is wrong.
If you put a non-digit character literal like xx it works, digit literals don't.
Both of these fail:
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'00'");
System.out.println(sdf.parse("20150100"));
Exception in thread "main" java.text.ParseException: Unparseable date:
"20150100" at java.text.DateFormat.parse(DateFormat.java:366)
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'00'");
System.out.println(dateTimeFormatter.parse("20150100", YearMonth::from));
Exception in thread "main" java.time.format.DateTimeParseException:
Text '20150100' could not be parsed at index 0 at
java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at
java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
Both of these succeed:
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM'xx'");
System.out.println(sdf.parse("201501xx"));
Thu Jan 01 00:00:00 EST 2015
final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMM'xx'");
System.out.println(dateTimeFormatter.parse("201501xx", YearMonth::from));
2015-01
If you don't mind to use a 3rd-party-library then you might try my library Time4J whose newest version v4.18 can do what you wish:
import net.time4j.Month;
import net.time4j.range.CalendarMonth;
import net.time4j.format.expert.ChronoFormatter;
import net.time4j.format.expert.PatternType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.text.ParseException;
import java.util.Locale;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
#RunWith(JUnit4.class)
public class CalendarMonthTest {
#Test
public void parse2() throws ParseException {
assertThat(
ChronoFormatter.ofPattern(
"yyyyMM'00'",
PatternType.CLDR,
Locale.ROOT,
CalendarMonth.chronology()
).parse("20150100"),
is(CalendarMonth.of(2015, Month.JANUARY)));
}
}
By the way, the links to the JDK-bug-log are not really related to your problem. Those issues only describe problems when applying adjacent digit parsing in context of fractional seconds. While that problem will be fixed with Java-9, your problem will not. Maybe you wish to open a new issue there? But I doubt that Oracle will treat it as bug. It is rather a new feature not supported until now by any library distributed by Oracle. Literals with (leading) digits are not expected in JSR-310 (aka java.time-package) to take part into adjacent-value-parsing (and in SimpleDateFormat also not).
Side note: Time4J is not just an answer to this detail (digit literals) but generally offers better performance in parsing and can be used in parallel with JSR-310 due to a lot of conversion methods. For example: To achieve an instance of YearMonth, just call calendarMonth.toTemporalAccessor() on the parsed result.
As an addendum to user177800's answer, you can use this form instead:
var formatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 4)
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral("00")
.toFormatter();
YearMonth.parse("20220200", formatter);
All part of java.time.

Joda wrong offset when using ZoneInfoProvider

Using Joda 2.1 in Java:
I have downloaded the latest timezone definitions 2013g, used ZoneInfoCompiler and have compiled those and moved them to my project's classpath.
I then call DateTimeZone.setProvider(new ZoneInfoProvider( "my-path-to-new-compiled-definitions" ) )
then when I try to instantiate new DateTime(DateTimeZone.forID("Asia/Jerusalem")) I get the time 1 hour backwards wrong.
It's as if compiling the new timezone definitions for joda and providing them has done nothing. However to test that it is actually using my new location, I tried executing DateTimeZone.forID("Asia/Khandyga") which did not exist in 2.1 and now retrieves and instantiates properly.
Ideas?

Categories

Resources