Thymeleaf, get current date and subtract x amount of days - spring

I am trying to implement an if statement on my Thymeleaf template that will change the colour of a value based on the current time (minus a specific amount of days).
Now from my understanding there are three ways to declare a date in Thymeleaf:
//For the new LocalDateTime, LocalDate classes
#temporals.createNow()
//For an instance of java.util.Date
#dates.createNow()
//For an instance of java.util.Calendars
#calendars.createNow()
Now my model uses instances of java.util.LocalDate so I tried tackling the problems in two different ways (unsuccessfully).
The first thing that come into my mind was to implement the following:
td th:if="${user.expiry_date.isBefore(#temporals.createNow().minus(7, ChronoUnit.DAYS))}"
th:text="${#dates.format(user.expiry_date, 'dd-MM-yyyy')}"style="color: red"/>
But I get the following SpelEvaluationException:
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'DAYS' cannot be found on null
The other approach would be to subtract the date on the server and pass the value through a variable:
//method in the #Controller class
model.addAttribute("userList", organisationService.getAllOrganisations());
**model.addAttribute("localDateNow", LocalDate.now().minusDays(7));**
But even then, accessing another variable inside the same spel expression seems impossible, or at least all my attempts failed:
<td th:if="${user.expiry_date.isBefore(localDateNow)}"
th:text="${#dates.format(user.expiry_date, 'dd-MM-yyyy')}"style="color: orange"/>
and:
<td th:if="${user.expiry_date.isBefore(${localDateNow})}"
th:text="${#dates.format(user.expiry_date, 'dd-MM-yyyy')}"style="color: orange"/>

For the first case, you can't directly call classes by name I believe. you'd have to do "${user.expiry_date.isBefore(#temporals.createNow().minus(7, T(java.time.temporal.ChronoUnit).DAYS))}"
And for the second I think you should be using ${#temporals.format()} as you'r using LocalDateTime not the clasic Date object but as theres no stacktrace given this is just a guess

Related

Is there a way to configure 2 property system in same #Value ins spring boot?

I trying to use 2 different property files with the same parameters which every parameter is describing the same property for example:
NewsPaperConsumer.properties, MarketConsumer.properties when every file have the same parameters.
My aim is to use the separated way to make the configuration files more readable but at the progromatic side union them to one hash map for example:
NewPaperConsumer and MarketConsumer have the parameter serverAddress so I'll get it by:
#Value("${serverAddress}")
private HashMap<String,String> serverAdresses;
how I change the way the system property save the parameter (instead of assign the value to string that It will assign it to hash map - {"key" : "value }

Problem detecting and using a nullable value in Freemarker

I have a POJO object that I have serialized from JSON (in Java). I am using an object wrapper constructed via:
DefaultObjectWrapperBuilder builder = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27);
builder.setExposeFields(true);
objectWrapper = builder.build();
I use the setExposeFields(true) because the object I am wrapping is not a Java bean, but rather just a POJO that contains public fields.
I am doing the following in my template:
<#ConditionOccurrence co = c/>
<#macro ConditionOccurrence co>
<#list co?keys as key>
${key}
</#list>
${co.occurrenceStartDate!'wtf'}
${co["occurrenceStartDate"]}
A condition occurrence of: ${codesetName(co.codesetId, "any condition")}
<#if co.first!false>- for the first time in the person's history</#if>
<#if (co["occurrenceStartDate"])??>co.OSD is null: </#if>
</#macro>
Note, the 'c' is an element in a sequence, and is not important to the exact problem I am having.
The output of the template shows this:
stopReason
getClass
gender
CorrelatedCriteria
providerSpecialty
occurrenceStartDate
occurrenceEndDate
visitType
accept
codesetId
hashCode
conditionSourceConcept
equals
conditionType
toString
conditionTypeExclude
class
first
age
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
A condition occurrence of: Psoriasis
- for the first time in the person's history
co.OSD is null:
The first set of lines are all the keys in my POJO. This is correct.
the two lines of output:
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
org.ohdsi.circe.cohortdefinition.DateRange#68e62ca4
This is showing that the field occurrenceStartDate is an object of type DateRange. note this could be null in some cases, so I am checking how to check for null...
The next part of the output:
- for the first time in the person's history
co.OSD is null:
This is showing that it is reading the 'first' attribute of the object correctly, and I have switched the raw JSON from 'true' to 'false' and the template responds properly to the change in this value. Note, in the object, the 'first' field is type Boolean.
The second line: co.OSD is null is what is confounding me. I confirmed earlier that outputting the 'occurrenceStartDate' field shows that it holds a DateRange object. But, this statement is evaluating to TRUE (ie: it is null):
#if (co["occurrenceStartDate"])??>co.OSD is null: </#if>
I have tried with both dot notation and bracket notation. For some reason, the ?? operator on that field is saying it is null. Note, the underlying object isn't a simple String or Number type, it is a simple POJO class DateRange with 3 String properties on it. Again, these are not JavaBeans, these are just POJOs.
Can anyone explain why the ?? operator says it is empty when it is clearly referencing an object? Btw: if I attempt to access co.occurrenceStartDate at all, it results in a template error that I'm referencing a null value, so the core problem here is why does the wrapper thing it is a null?
Thank you in advance for your help.
The ?? operator means "is present", not "is missing". So your line should be:
<#if !(co.occurrenceStartDate??)>co.OSD is null: </#if>

How to get value from a column referenced by a number, from JDBC Response object of Jmeter?

I know they advice to get a cell value this way:
columnValue = vars.getObject("resultObject").get(0).get("Column Name");
as stated on jMeter doc : component reference : JDBC_Request.
But: How to access the same RS cell value by just a number of the column?
RS.get(0).get(4);
...instead of giving it a String of column Name/Label.
edit 1: Lets use Groovy/Java, instead of BeanShell. Thanks.
edit 2: The original motivation was the difference between column Name / Label, as these seem to be not fully guaranteed (? seems to be not clear here, not to me), especially due case-sensitivity ("id"/"ID", "name"/"Name"/"NAME" ..)
It should be something like:
String value = (new ArrayList<String>(vars.getObject("resultObject").get(0).values())).get(4)
More information: Debugging JDBC Sampler Results in JMeter
Be aware that according to HashMap documentation:
This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
So the order of columns might be a big question mark.
The row itself is a HashMap, defined in source code as:
HashMap<String, Object> row
So using BeanShell syntax, you could get it as
row = vars.getObject("resultObject").get(0); // returns HashMap
In HashMap, you cannot access item (column) by ID. You could, however, apply one of the methods described here, but HashMap doesn't guarantee order, so you cannot be sure what "column 4" will contain.
If you want to be able to loop through all columns, it's better to do it in a Map style, not by index. For example using entrySet() with BeanShell:
for(Map.Entry entry : row.entrySet())
{
log.info(entry.getKey() + "=" + entry.getValue());
}
See various ways to iterate through Map here.

Why does Java 8 DateTimeFormatter allows an incorrect month value in ResolverStyle.STRICT mode?

Why does this test pass, while the month value is obviously invalid (13)?
#Test
public void test() {
String format = "uuuuMM";
String value = "201713";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
When using a temporal query, the expected DateTimeParseException is thrown:
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value, YearMonth::from);
What happens when no TemporalQuery is specified?
EDIT: the 13 value seems to be a special one, as I learned thanks to the answer of ΦXocę 웃 Пepeúpa ツ (see Undecimber).
But the exception is not thrown even with another value, like 50:
#Test
public void test() {
String format = "uuuuMM";
String value = "201750";
DateTimeFormatter.ofPattern(format).withResolverStyle(ResolverStyle.STRICT)
.parse(value);
}
I've made some debugging here and found that part of the parsing process is to check the fields against the formatter's chronology.
When you create a DateTimeFormatter, by default it uses an IsoChronology, which is used to resolve the date fields. During this resolving phase, the method java.time.chrono.AbstractChronology::resolveDate is called.
If you look at the source, you'll see the following logic:
if (fieldValues.containsKey(YEAR)) {
if (fieldValues.containsKey(MONTH_OF_YEAR)) {
if (fieldValues.containsKey(DAY_OF_MONTH)) {
return resolveYMD(fieldValues, resolverStyle);
}
....
return null;
As the input has only the year and month fields, fieldValues.containsKey(DAY_OF_MONTH) returns false, the method returns null and no other check is made as you can see in the Parsed class.
So, when parsing 201750 or 201713 without a TemporalQuery, no additional check is made because of the logic above, and the parse method returns a java.time.format.Parsed object, as you can see by the following code:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMM").withResolverStyle(ResolverStyle.STRICT);
TemporalAccessor parsed = fmt.parse("201750");
System.out.println(parsed.getClass());
System.out.println(parsed);
The output is:
class java.time.format.Parsed
{Year=2017, MonthOfYear=50},ISO
Note that the type of the returned object is java.time.format.Parsed and printing it shows the fields that were parsed (year and month).
When you call parse with a TemporalQuery, though, the Parsed object is passed to the query and its fields are validated (of course it depends on the query, but the API built-in ones always validate).
In the case of YearMonth::from, it checks if the year and month are valid using the respective ChronoField's (MONTH_OF_YEAR and YEAR) and the month field accepts only values from 1 to 12.
That's why just calling parse(value) doesn't throw an exception, but calling with a TemporalQuery does.
Just to check the logic above when all the date fields (year, month and day) are present:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuuMMdd").withResolverStyle(ResolverStyle.STRICT);
fmt.parse("20175010");
This throws:
Exception in thread "main" java.time.format.DateTimeParseException: Text '20175010' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 50
As all the date fields are present, fieldValues.containsKey(DAY_OF_MONTH) returns true and now it checks if it's a valid date (using the resolveYMD method).
The month 13 is called : Undecimber
The gregorian calendar that many of us use allows 12 months only but java includes support for calendars which permit thirteen months so it depends on what calendar system you are talking about
For example, the actual maximum value of the MONTH field is 12 in some years, and 13 in other years in the Hebrew calendar system. So the month 13 is valid
It is a little odd that an exception is not thrown when parse is called without a given TemporalQuery. Some of the documentation for the single argument parse method:
This parses the entire text producing a temporal object. It is typically more useful to use parse(CharSequence, TemporalQuery). The result of this method is TemporalAccessor which has been resolved, applying basic validation checks to help ensure a valid date-time.
Note that it says it is "typically more useful to use parse(CharSequence, TemporalQuery)". In your examples, parse is returning a java.time.format.Parsed object, which is not really used for anything other than creating a different TemporalAccessor.
Note that if you try to create a YearMonth from the returned value, an exception is thrown:
YearMonth.from(DateTimeFormatter.ofPattern(format)
.withResolverStyle(ResolverStyle.STRICT).parse(value));
throws
Exception in thread "main" java.time.DateTimeException: Unable to obtain YearMonth from TemporalAccessor: {Year=2017, MonthOfYear=50},ISO of type java.time.format.Parsed
at java.time.YearMonth.from(YearMonth.java:263)
at anl.nfolds.Test.main(Test.java:21)
Caused by: java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 50
at java.time.temporal.TemporalAccessor.get(TemporalAccessor.java:224)
at java.time.YearMonth.from(YearMonth.java:260)
... 1 more
Documentation for Parsed:
A store of parsed data.
This class is used during parsing to collect the data. Part of the parsing process involves handling optional blocks and multiple copies of the data get created to support the necessary backtracking.
Once parsing is completed, this class can be used as the resultant TemporalAccessor. In most cases, it is only exposed once the fields have been resolved.
Since:1.8
#implSpecThis class is a mutable context intended for use from a single thread. Usage of the class is thread-safe within standard parsing as a new instance of this class is automatically created for each parse and parsing is single-threaded

Get Month Name of java.time.chrono.HijrahDate instance

HijrahDate hd=HijrahChronology.INSTANCE.date(LocalDate.of(2014,11, 25));
If we have HijrahDate Instance , it is expected to have a method in UmalquraCalendar API that shows the name of month :
i inspect properties of this instance using groovy API :
['era':AH,
'class':class java.time.chrono.HijrahDate,
'prolepticMonth':17233,
'eraValue':1,
'dayOfWeek':2,
'leapYear':false,
'chronology':Hijrah-umalqura,
'dayOfYear':32]
However we don't find the month name which must be one of the following list items :
Muḥarram (محرم meaning "forbidden"), so called because battle was
forbidden (haram) during this month. Muharram includes the Day of
Ashura.
Ṣafar (صفر meaning "void"), supposedly named thus because
pagan Arab houses were empty this time of year while their occupants
gathered food.
Rabīʿ I (Rabīʿ al-Awwal, ربيع الأوّل) meaning "the
first spring".
Rabīʿ II (Rabīʿ ath-Thānī ربيع الثاني or Rabīʿ al-Ākhir
ربيع الآخ
.....................
............ so on SEE
Thus , since there is no attribute save month's name , it is expwcted to have a method retrieve this info ?
What's this method?
The date does not contain information about the names of the months or days. To get that you need a formatter:
System.out.println(DateTimeFormatter.ofPattern("MMMM").format(hd));
prints Safar.
Since the main language of UmalQura is the arabic langugage, Developers & programmers who uses UmalQuraCalender want to display the month in arabic. Thus , we base on #assylias answer we can add the Locale object to print صفر instead of Safar
System.out.println(DateTimeFormatter.ofPattern("MMMM").format(hd,new Locale("ar")));
public String getIslamicDate(){
return DateTimeFormatter.ofPattern("MMMM",new Locale("ar")).format(HijrahDate.now());
}
I think this should work just fine and return the month in arabic language

Resources