TimeBasedAnomalyRule.java
package com.hsbc.fraud.rules.impl;
import com.hsbc.fraud.model.AlertType;
import com.hsbc.fraud.model.Transaction;
import com.hsbc.fraud.rules.FraudRule;
import com.hsbc.fraud.rules.RuleResult;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.Optional;
/**
* Rule to detect transactions at unusual times (e.g., late night transactions).
* Typically, legitimate transactions are less common during certain hours.
*/
public class TimeBasedAnomalyRule implements FraudRule {
private static final long serialVersionUID = 1L;
private final LocalTime nightStartTime;
private final LocalTime nightEndTime;
private final ZoneId zoneId;
public TimeBasedAnomalyRule() {
this(LocalTime.of(1, 0), LocalTime.of(5, 0), ZoneId.of("UTC"));
}
public TimeBasedAnomalyRule(LocalTime nightStartTime, LocalTime nightEndTime, ZoneId zoneId) {
this.nightStartTime = nightStartTime;
this.nightEndTime = nightEndTime;
this.zoneId = zoneId;
}
@Override
public String getRuleName() {
return "TIME_BASED_ANOMALY";
}
@Override
public String getDescription() {
return "Detects transactions occurring during unusual hours";
}
@Override
public Optional<RuleResult> evaluate(Transaction transaction) {
if (transaction == null || transaction.getTimestamp() == null) {
return Optional.empty();
}
LocalTime transactionTime = LocalTime.from(
transaction.getTimestamp().atZone(zoneId)
);
if (isWithinNightHours(transactionTime)) {
return Optional.of(new RuleResult(
getRuleName(),
AlertType.TIME_BASED_ANOMALY,
0.4,
String.format("Transaction at unusual time: %s (Night hours: %s - %s)",
transactionTime, nightStartTime, nightEndTime)
));
}
return Optional.empty();
}
private boolean isWithinNightHours(LocalTime time) {
if (nightStartTime.isBefore(nightEndTime)) {
// Normal case: e.g., 1:00 AM to 5:00 AM
return !time.isBefore(nightStartTime) && time.isBefore(nightEndTime);
} else {
// Crosses midnight: e.g., 11:00 PM to 5:00 AM
return !time.isBefore(nightStartTime) || time.isBefore(nightEndTime);
}
}
@Override
public int getPriority() {
return 30;
}
public LocalTime getNightStartTime() {
return nightStartTime;
}
public LocalTime getNightEndTime() {
return nightEndTime;
}
}