Sorting Days of the week with their time - java

I want the sort starting on Sunday at 00:00. My problem is that the zero of this code starts on Thursday at 8:00. I sort by getting the minutes, but zero starts at Thu 8:00.
This is my code so far.
String[] lines = S.split("\n");
System.out.println(Arrays.toString(lines));
int longest = 0;
try {
for (int i = 0; i < lines.length - 1; i++) {
SimpleDateFormat formatter = new SimpleDateFormat("EEE HH:mm-HH:mm", Locale.US);
long diffMinutes;
diffMinutes = TimeUnit.MILLISECONDS.toMinutes(
formatter.parse(lines[i + 1]).getTime() -
formatter.parse(lines[i]).getTime());
if (longest < diffMinutes) {
longest = (int) diffMinutes;
}
}
} catch (ParseException e) {
e.printStackTrace();
}
return longest;
The function takes a String like this
"Mon 01:00-23:00\\nTue 01:00-23:00\\nWed 01:00-23:00\\nThu 01:00-23:00\\nFri 01:00-23:00\\nSat 01:00-23:00\\nSun 01:00-21:00"
The program slices the string and stores it in array lines, then I'm trying to sort it.

I think this problem may be solved by applying some more splitting, due to your String representation of the period being of the form from-to instead of an absolute value like 1320 (minutes). I would use java.time, especially LocalTime and Duration for proper calculating and comparing.
My code basically does the following (see code comments) making use of helper methods:
splits the input by "\n"
splits every result of the first step by space in order to separate the day of week from the times of day
splits the second result of the second split by "-" in order to get the times of day
converts the results into appropriate objects
stores them in an appropriate data structure
finds the maximum durations
This is what I came up with:
import java.time.DayOfWeek;
import java.time.Duration;
import java.time.LocalTime;
import java.time.format.TextStyle;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
class WeekdayDurationHandler {
public static void main(String[] args) {
// input String splitting...
String input = "Mon 01:00-23:00\nTue 01:00-23:00\nWed 01:00-23:00\nThu 01:00-23:00\nFri 01:00-23:00\nSat 01:00-23:00\nSun 01:00-21:00";
String[] times = input.split("\n");
// data structure for holding the durations per day
Map<DayOfWeek, Duration> weekdayDurations = new TreeMap<>();
// the result of the first splitting is unparseable, that's why some more
// splitting is applied
for (String s : times) {
// separate the day of week from the time duration
String[] sp = s.split(" ");
// split the duration into "from" and "to" (time of day)
String[] tp = sp[1].split("-");
// parse the day of week into an appropriate object
DayOfWeek dayOfWeek = parseDayOfWeek(sp[0]);
// parse the times of day into appropriate objects
LocalTime localTimeFrom = LocalTime.parse(tp[0]);
LocalTime localTimeTo = LocalTime.parse(tp[1]);
// calculate the duration between "from" and "to" time of day
Duration duration = Duration.between(localTimeFrom, localTimeTo);
// store them in the data structure
weekdayDurations.put(dayOfWeek, duration);
}
// print them
weekdayDurations.forEach((DayOfWeek dayOfWeek, Duration duration) -> {
System.out.println(dayOfWeek.getDisplayName(TextStyle.FULL_STANDALONE, Locale.getDefault()) + ": "
+ duration.toHours() + " hours (" + duration.toMinutes() + " minutes)");
});
System.out.println("######################################################");
// then print the maximum durations found
findMaxDurationsFrom(weekdayDurations).forEach((DayOfWeek dayOfWeek, Duration duration) -> {
System.out.println(dayOfWeek.getDisplayName(TextStyle.FULL_STANDALONE, Locale.getDefault()) + ": "
+ duration.toHours() + " hours (" + duration.toMinutes() + " minutes)");
});
}
private static DayOfWeek parseDayOfWeek(String weekday) {
switch (weekday.toLowerCase()) {
case "mon":
return DayOfWeek.MONDAY;
case "tue":
return DayOfWeek.TUESDAY;
case "wed":
return DayOfWeek.WEDNESDAY;
case "thu":
return DayOfWeek.THURSDAY;
case "fri":
return DayOfWeek.FRIDAY;
case "sat":
return DayOfWeek.SATURDAY;
case "sun":
return DayOfWeek.SUNDAY;
default:
throw new RuntimeException("Unparsable weekday: \"" + weekday + "\"");
}
}
private static Map<DayOfWeek, Duration> findMaxDurationsFrom(Map<DayOfWeek, Duration> weekDurations) {
final Map<DayOfWeek, Duration> maxDurations = new TreeMap<>();
// find the maximum duration as a reference for all equal durations
Duration maxDuration = findMaxDuration(weekDurations);
// go through all durations and store those equal to maxDuration (no matter what day of week)
weekDurations.forEach((DayOfWeek dayOfWeek, Duration duration) -> {
if (duration.equals(maxDuration)) {
maxDurations.put(dayOfWeek, duration);
}
});
return maxDurations;
}
private static <K, V extends Comparable<V>> V findMaxDuration(Map<K, V> map) {
Entry<K, V> maxEntry = Collections.max(map.entrySet(),
(Entry<K, V> e1, Entry<K, V> e2) -> e1.getValue().compareTo(e2.getValue()));
return maxEntry.getValue();
}
}
I hope it helps…

Related

Find the remaining months in a year

I am trying to find a way to get an array of month for a year(Ex:2017)
The array to be the remaining months in a year only
Is this possible, What is the best way to achieve this
I searched for the answer couldn't find it
ex:{"October","November","December"}
What I have done so far: I am able to get current year
Date now = new Date();
int year = now.getYear();
You should better use new API LocalDate rather than Date, it's easier to use
first, you need to get the current date : here it's 07/10/201
you need to find out many month there is until the end of the year : here it's 2
then you create an array of the size + 1 (+1 is for current month) : here size is 3
then you fill the array with the month of date you have + i month
LocalDate date = LocalDate.now();
int nbMonthRemain = 12 - date.getMonth().getValue();
String[] monthsRemaining = new String[nbMonthRemain + 1];
for (int i = 0; i < monthsRemaining.length; i++) {
monthsRemaining[i] = date.plusMonths(i).getMonth().toString();
}
System.out.println(Arrays.toString(monthsRemaining)); // [OCTOBER, NOVEMBER, DECEMBER]
Tips :
Replace .toString() by :
.getDisplayName(TextStyle.FULL, Locale.ENGLISH); to get [October, November, December]
.getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH); to get [10, 11, 12]
.getDisplayName(TextStyle.SHORT, Locale.ENGLISH); to get [Oct, Nov, Dec]
...
Use GregorianCalendar.getInstance().get(Calendar.MONTH) to get current month number (For oct you will get 9)
i believe something like below code could help
import java.text.DateFormatSymbols;
import java.util.List;
import java.util.ArrayList;
public List GetLeftMonth(Integer Mon_num){
List<String> monthsList = new ArrayList<String>();
String[] months = new DateFormatSymbols().getMonths();
for (int i = Mon_num; i < months.length; i++) {
String month = months[i];
System.out.println("month = " + month);
monthsList .add(months[i]);
}
return monthsList;
}
public static void main(String[] args) {
LocalDate now = LocalDate.now();
int currentMonth = now.getMonthValue();
int monthsInAYear = 12;
int monthsRemaining = monthsInAYear - currentMonth;
System.out.println(monthsRemaining); // 2
for (int i = currentMonth; i <= monthsInAYear; i++) {
System.out.println(getMonthName(i)); // OCTOBER NOVEMBER DECEMBER
}
}
private static Month getMonthName(int monthValue) {
return Month.of(monthValue);
}
Don't forget to import import java.time.LocalDate;
import java.time.Month;
As already said, don't use java.util.Date but java.time.LocalDate instead, since it is (arguably) easier to work with.

String Time to actual Time

what I am trying to do is create an itinerary of flight paths using JGraphT. The problem I am facing is converting the string times I have set into actual times that I can then do calculations over multiple days i.e. if a flight leaves at 16.00 and arrives at 18.30 but then the connecting flight leaves at 14.00 and arrives at the final destination at 16.00 it will have been 24 hours (i.e. one day). I am lost in this as I have tried to parse the string to a date in the Flight class and also used The simple date format which is causing errors.
My code as follows;
Flight3.java
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.StringTokenizer;
import org.jgrapht.alg.DijkstraShortestPath;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.SimpleDirectedWeightedGraph;
public class Flight3
{
private static ArrayList<String[]> myEdges;
private static ArrayList<Flight> flight;
public Flight3()
{
}
public static void main(String [] args)
{
myEdges = new ArrayList<String[]>();
flight = new ArrayList<Flight>();
SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> Graph = createGraph();
System.out.println("Airlines!");
Scanner sc = new Scanner(System.in);
System.out.println("Enter the airport you wish to fly from");
String startVertex = sc.nextLine();
while(!Graph.containsVertex(startVertex))
{
System.out.println("Sorry, that airport does not exist. Please select another;");
startVertex = sc.nextLine();
}
System.out.println("Enter destination airport");
String endVertex = sc.nextLine();
while(!Graph.containsVertex(endVertex))
{
System.out.println("Sorry, that airport does not exist. Please select another;");
endVertex = sc.nextLine();
}
calculatePath(Graph, startVertex, endVertex);
}
private static SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> createGraph()
{
SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> g =
(SimpleDirectedWeightedGraph<String, DefaultWeightedEdge>) new SimpleDirectedWeightedGraph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
{
g.addVertex("London");
g.addVertex("France");
g.addVertex("Spain");
createTwoWayWeightedEdge(g, "London", "France", 80);
generateFlight("1600", "1830", "EH445", "0000", 80);
generateFlight("0400", "0600", "HE452", "0000", 80);
createTwoWayWeightedEdge(g, "France", "Spain", 130);
generateFlight("1400", "1600", "HD123", "0400", 130);
generateFlight("0400", "0600", "DH712", "0000", 130);
}
return g;
}
private static void createTwoWayWeightedEdge(SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> g, String vertex1, String vertex2, double weight)
{
g.addEdge(vertex1, vertex2);
g.addEdge(vertex2, vertex1);
g.setEdgeWeight(g.getEdge(vertex1, vertex2), weight);
g.setEdgeWeight(g.getEdge(vertex2, vertex1), weight);
String[] tmp1 = {vertex1, vertex2};
myEdges.add(tmp1);
String[] tmp2 = {vertex2, vertex1};
myEdges.add(tmp2);
}
private static void generateFlight(String depTime, String arrTime, String flightNo, String locTime, int duration)
{
Flight f = new Flight(depTime, arrTime, flightNo, locTime, duration);
flight.add(f);
}
private static String textToPrint(String[] format)
{
String text = " ";
for(int i = 0; i < format.length; i++)
{
switch(i)
{
case 0:
text = text + format[i];
for(int j = format[i].length(); j < 6 ; j++)
text = text + " ";
break;
case 1:
text = text + format[i];
for(int j = format[i].length(); j < 15 ; j++)
text = text + " ";
break;
case 2:
text = text + format[i];
for(int j = format[i].length(); j < 10 ; j++)
text = text + " ";
break;
case 3:
text = text + format[i];
for(int j = format[i].length(); j < 10 ; j++)
text = text + " ";
break;
case 4:
text = text + format[i];
for(int j = format[i].length(); j < 15 ; j++)
text = text + " ";
break;
case 5:
text = text + format[i];
for(int j = format[i].length(); j < 10 ; j++)
text = text + " ";
break;
case 6:
text = text + format[i];
for(int j = format[i].length(); j < 10 ; j++)
text = text + " ";
break;
}
}
return text;
}
private static void calculatePath(SimpleDirectedWeightedGraph<String, DefaultWeightedEdge> g, String startVertex, String endVertex)
{
DijkstraShortestPath path = new DijkstraShortestPath(g, startVertex, endVertex);
path.getPath();
List<Object> edges = path.getPathEdgeList();
String item;
int count = 1;
double totalDuration = 0;
if(edges!=null)
{
System.out.println("\n The lowest cost route is:");
String[] labels = {"Flight.", "Leave from", "At", "On", "Arrive", "At", "Local Time"};
System.out.println(textToPrint(labels));
for(Object edge : edges)
{
item = edge.toString();
StringTokenizer st = new StringTokenizer(item, ":");
String firstAirport = st.nextToken().trim().substring(1);
String secondAirport = st.nextToken().trim();
secondAirport = secondAirport.substring(0, secondAirport.length()-1);
String depTime = null;
String arrTime = null;
String flightNo = null, locTime = null;
double price, flightDuration;
for(int i=0;i<flight.size();i++)
{
if(firstAirport.equals(myEdges.get(i)[0]) && secondAirport.equals(myEdges.get(i)[1]))
{
Flight details = flight.get(i);
flightNo = details.flightNo;
depTime = details.depTime;
arrTime = details.arrTime;
price = details.price;
flightDuration = details.duration;
totalDuration = totalDuration + details.getDuration();
locTime = details.getLocTime();
String[] flightInfo = {count+".", firstAirport, depTime, flightNo, secondAirport, arrTime, locTime};
System.out.println(textToPrint(flightInfo));
}
}
count++;
}
System.out.println("Cost of route = £"+path.getPathLength());
System.out.println("Total time in the air = "+totalDuration +"hrs");
}
else
System.out.println("Sorry you can't fly there from " + startVertex);
}
Flight.java
import java.text.SimpleDateFormat;
public class Flight {
String depTime;
String arrTime;
String flightNo;
String locTime;
double duration;
int price;
public Flight(String depTime, String arrTime, String flightNo, String locTime, int duration){
this.depTime = depTime;
this.arrTime = arrTime;
this.flightNo = flightNo;
this.locTime = locTime;
this.duration = duration;
}
public double getDuration(){
double duration = Integer.parseInt(arrTime) - Integer.parseInt(depTime);
return duration / 100;
}
public String getLocTime(){
int value = Integer.parseInt(locTime) + Integer.parseInt(arrTime);
locTime = ""+value;
return locTime;
}
public String getFlightNo(){
return flightNo;
}
public double getPrice(){
return price;
}
}
Your Question is not exactly clear. But here are some general tips.
Departure + Duration = Arrival
Do not focus on arrival. The arrival time is a result of [arrival + duration]. In other words, output not input. This solves your problem with rotating around the clock crossing midnight.
Use objects, not strings
Use objects, not strings. Java has an excellent industry-leading date-time framework in the java.time classes. Use them. But do not use the notoriously troublesome old legacy date-time classes, the ones outside the java.time package. Use strings only as needed in the user-interface and for serializing data.
In particular, you should be looking at the LocalTime and Duration classes. See Oracle Tutorial.
ISO 8601
And for serializing, stick to the standard ISO 8601 formats such as HH:MM for a time-only value with the colon being optional for the “basic” version though I recommend keeping the colon for the “extended” version. The java.time classes use the extended version of ISO 8601 by default when parsing and generating strings that represent date-time values.
Make Flight ignorant of charting, and vice-versa
Separate out your data model from the chart drawing. Define Flight as just the flight info and functions, without thinking about the charting.
class Flight {
LocalTime departure;
Duration duration;
LocalTime getArrival() {
LocalTime arrival = departure.plus( duration );
// If called *many* times, and you account for changing-data and thread-safety, you could cache this result for performance.
return arrival;
}
Flight( LocalTime departureArg , Duration durationArg ) {
this.departure = departureArg;
this.duration = durationArg;
}
}
To produce the exact data needed by chart, either use getter methods on Flight or a mediating class between Flight and chart. Keep the classes as cleanly separated as possible. Your car’s radio-stereo does not need to know anything about the air-conditioner which in turn does not need to know about the engine’s fuel-oxygen mixture ratio.
Time Zone
The big elephant in the room is time zone. You have not made clear your intentions here.
Generally best to work in UTC. Translate to a local time zone only as needed for presentation to the user.
Not much more to say on this until you clarify the intention of your business context.
Search Stack Overflow for the Java classes LocalDateTime, OffsetDateTime, ZonedDateTime, ZoneOffset, and ZoneId to learn much more.
We might have to append/prefix some format of today's date (examples here : https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html) before doing the duration computation.
so when we have the start time of "0430" we will prefix some format of today's date as well . For example:
startDate = "0430"
becomes
startDateModified = new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + startDate;
startDateObject = new SimpleDateFormat("yyyy/MM/dd HH:mm").parse(statDateModified)
similarly we do so for the end date (we might have to use some other value of new Date() because the arrival time be for the next day etc)
And then we do the computation.
Now regarding your question, we will have to some how figure out the connecting flight departure date value is not
new Date(); //today
but
new Date(System.currentTimeMillis() + 24*3600); //tomorrow
We might, in fact, have to have another shorthand notation to denote that the flight leaves today and reaches tomorrow (like "+1 0430" to denote +1 day etc)

Count days between two dates with Java 8 while ignoring certain days of week

Below I have 3 methods. The first is very simple. It just counts the total number of days. The second, however, will not only count the days, but will ignore the days of the week that are passed in to the method.
My problem is that the third method is not always correct. It should match the second method. I am guessing it has something to do with leap years, because the difference is usually +=3|4 when it is incorrect.
Additional Info
I am attempting to mock Excel's weekday(serial_number,[return_type]) formula in a way.
serial_number = startDate:Date - daysOfWeekToInclude:Array<Integer>
Example
| A | B | C
+---------+----------------------------------------------------+-----------
1 | Start | =DATE(2014,9,7) | 9/7/2014
2 | End | =DATE(2025,6,13) | 6/13/2025
3 | Include | ={1,2,4,6} (Mon, Tue, Thu, & Sat) | <Disp Only>
4 | Days | =SUM(INT((WEEKDAY($B$1-{1,2,4,6},1)+$B$2-$B$1)/7)) | 2248
There is more information on this function here: How to count / calculate the number of days between two dates in Excel?
Raw Image
Methods
Simply count the number of days between two dates.
public static int simpleDaysBetween(final LocalDate start,
final LocalDate end) {
return (int) ChronoUnit.DAYS.between(start, end);
}
Count number of days, ignoring certain days of week, using a loop.
public static int betterDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int count = 0;
LocalDate curr = start.plusDays(0);
while (curr.isBefore(end)) {
if (!ignore.contains(curr.getDayOfWeek())) {
count++;
}
curr = curr.plusDays(1); // Increment by a day.
}
return count;
}
Count number of days. again but without a loop.
public static int bestDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int days = simpleDaysBetween(start, end);
if (days == 0) {
return 0;
}
if (!ignore.isEmpty()) {
int weeks = days / 7;
int startDay = start.getDayOfWeek().getValue();
int endDay = end.getDayOfWeek().getValue();
int diff = weeks * ignore.size();
for (DayOfWeek day : ignore) {
int currDay = day.getValue();
if (startDay <= currDay) {
diff++;
}
if (endDay > currDay) {
diff++;
}
}
if (endDay > startDay) {
diff -= endDay - startDay;
}
return days - diff;
}
return days;
}
Full code
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;
public class DayCounter {
public static void main(String[] args) {
final LocalDate start = LocalDate.of(2014, 9, 7);
final LocalDate end = LocalDate.of(2025, 6, 13);
List<DayOfWeek> ignore = Arrays.asList(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY);
print(start);
print(end);
System.out.println(simpleDaysBetween(start, end));
System.out.println(betterDaysBetween(start, end, ignore));
System.out.println(bestDaysBetween(start, end, ignore));
}
public static void print(LocalDate date) {
System.out.printf("%s -> %s%n", date, date.getDayOfWeek());
}
public static int simpleDaysBetween(final LocalDate start,
final LocalDate end) {
return (int) ChronoUnit.DAYS.between(start, end);
}
public static int betterDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int count = 0;
LocalDate curr = start.plusDays(0);
while (curr.isBefore(end)) {
if (!ignore.contains(curr.getDayOfWeek())) {
count++;
}
curr = curr.plusDays(1); // Increment by a day.
}
return count;
}
public static int bestDaysBetween(final LocalDate start,
final LocalDate end, final List<DayOfWeek> ignore) {
int days = simpleDaysBetween(start, end);
if (days == 0) {
return 0;
}
if (!ignore.isEmpty()) {
int weeks = days / 7;
int startDay = start.getDayOfWeek().getValue();
int endDay = end.getDayOfWeek().getValue();
int diff = weeks * ignore.size();
for (DayOfWeek day : ignore) {
int currDay = day.getValue();
if (startDay <= currDay) {
diff++;
}
if (endDay > currDay) {
diff++;
}
}
if (endDay > startDay) {
diff -= endDay - startDay;
}
return days - diff;
}
return days;
}
}
If we talk about a Java 8 API, why not use the Java 8 features consequently…
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
return Stream.iterate(start, d->d.plusDays(1))
.limit(start.until(end, ChronoUnit.DAYS))
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
Starting with Java 9, we can use the even simpler
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
return start.datesUntil(end)
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
Though, it might be worth using a Set with a better-than-linear lookup rather than the List:
static long daysBetween(LocalDate start, LocalDate end, List<DayOfWeek> ignore) {
if(ignore.isEmpty()) return start.until(end, ChronoUnit.DAYS);
EnumSet<DayOfWeek> set = EnumSet.copyOf(ignore);
return start.datesUntil(end)
.filter(d->!ignore.contains(d.getDayOfWeek()))
.count();
}
You may consider changing the parameter to Set<DayOfWeek>, as it is not only more efficient but better suited to the actual use cases. Instead of Arrays.asList(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY), you can pass EnumSet.of(DayOfWeek.SUNDAY, DayOfWeek.WEDNESDAY, DayOfWeek.FRIDAY), but you can also use constructs like EnumSet.range(DayOfWeek.MONDAY, DayOfWeek.FRIDAY), to denote the typical working days.
You can avoid iterating over all days, but it requires special care about corner cases and hence, thorough testing. And will only pay off for really large ranges. For completeness, this is the optimized variant:
static long daysBetween(LocalDate start, LocalDate end, Set<DayOfWeek> ignore) {
long d1 = start.toEpochDay(), d2 = end.toEpochDay();
if(d1 > d2) throw new IllegalArgumentException();
if(ignore.isEmpty()) return d2 - d1;
int incompleteWeek = 0;
DayOfWeek startDoW = start.getDayOfWeek(), endDoW = end.getDayOfWeek();
if(startDoW != endDoW) {
for(int v1 = startDoW.getValue(), v2 = endDoW.getValue();
v1 != v2 && d1 < d2; v1 = v1%7+1, d1++) {
if(!ignore.contains(DayOfWeek.of(v1))) incompleteWeek++;
}
}
return incompleteWeek + (d2 - d1) * (7 - ignore.size()) / 7;
}
Here, the performance of the ignore set’s lookup doesn’t matter, as we only look up at most six values, however, enforcing a Set, i.e. no duplicates, allows us to use the set’s size to calculate the number of days contained in complete weeks of the range. Complete weeks have the same day of week for the start and (exclusive) end date. So the code only needs to iterate the days, until the start and end day of week match.
You you are using wrong Excel formula. See the section "Using SUM and INT function to count the number of workdays" of the site that you have provided. It is stating the formula as:
=SUM(INT((WEEKDAY(A2-{2,3,4,5,6})+B2-A2)/7))
In Excel, Sunday is 1 and Saturday is 7. The numbers inside the curly braces indicates the day-of-weeks to be included. So for your case the formula will be:
=SUM(INT((WEEKDAY(A2-{2,3,5,7})+B2-A2)/7))
Please see the attached screenshot:
It is returning 2247 as the following code returns:
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class SO25798876 {
public static void main(String[] args) {
String strStartDate = "09/07/2014";
String strEndDate = "06/13/2025";
String pattern = "MM/dd/yyyy";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
LocalDate startDate = LocalDate.parse(strStartDate, formatter);
LocalDate endDate = LocalDate.parse(strEndDate, formatter);
int count = 0;
while(startDate.isBefore(endDate) || startDate.isEqual(endDate)) { // you may want to not to use the isEqual method
DayOfWeek dayOfWeek = startDate.getDayOfWeek();
if(!(dayOfWeek == DayOfWeek.SUNDAY || dayOfWeek == DayOfWeek.WEDNESDAY || dayOfWeek == DayOfWeek.FRIDAY)) {
count++;
}
startDate = startDate.plusDays(1);
}
System.out.println(count);
}
}
You also have mentioned your doubt that the java.time may be not considering leap year, which is wrong, if you add the following piece of code
long year = startDate.getYear();
if(Year.isLeap(year)) {
Month month = startDate.getMonth();
if(month == Month.FEBRUARY && startDate.getDayOfMonth() == 29) {
System.out.println("Calculated 29th Feb for the year: " + year);
}
}
You will see that it is printing:
Calculated 29th Feb for the year: 2016
Calculated 29th Feb for the year: 2020
Calculated 29th Feb for the year: 2024
Lastly the count will be 2247 which matches the Excel result.
Happy coding.
-Tapas

Creating Java Program to Return Previous, Current, and Next Day

I am creating a final program for my introductory java programming course and I am in need of some assistance. The current day and the next day values are not correctly printing. I have read over my code again and again but I cannot wrap my head around why this is not running properly. Any help/advice is GREATLY appreciated!
The guidelines I must follow for this program assignment are as follows:
A. Set the day.
B. Print the day.
C. Return the day.
D. Return the next day.
E. Return the previous day.
F. Calculate and return the day by adding certain days to the current day. For example, if the current day is Monday and we add four days, the day to be returned is Friday. Similarly, if today is Tuesday and we add 13 days, the day to be returned is Monday.
G. Add the appropriate constructors.
H. Write the definitions of the methods to implement the operations for the class Day, as defined in A through G.
I. Write a program to test various operations on the class Day.
*EDIT***
This new code I have written works excellent until I set the day as Monday. The issue I am having is within the previousDay method. I'm trying to add an if/else statement but I cannot get it to compile correctly without receive an outOfBounds Exception error. What is a good way to accomplish this in a String array?
The UPDATED CODE I have written in attempt to accomplish this program task:
public class Day {
int index;
static String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
public Day(String currentDay)
{
for(int i = 0; i < days.length; i++)
{
if(currentDay.equals(days[i]))
{
index = i;
return;
}
}
System.out.println("Days is invalid");
}
public void printDay()
{
System.out.println("The Day is " + days[index]);
}
public String returnDay()
{
return days[index];
}
public String returnNextDay()
{
return days[(index + 1 )% days.length];
}
public String returnPreviousDay()
{
if (days.length <= 1)
return days[index + 6];
else return days[(index - 1)%days.length];
}
public String whatDayIs(int i)
{
return days[(index + i)%days.length];
}
public static void main(String[] args)
{
Day day = new Day("Mon");
day.printDay();
System.out.println("Return Day: " + day.returnDay());
System.out.println("Next Day: " + day.returnNextDay());
System.out.println("Previous Day: " + day.returnPreviousDay());
System.out.println(day.whatDayIs(7));
}
}
Thank you for your time!
Adam
First of all, use java naming conventions. It makes your code clearer and easier to help.
You have some errors in your code:
public Day setNameDay(String Day) {
Day = Days;
return this;
}
What you do here is saving Days value in method's parameter Day which actually does nothing.
public void nextDay() {
Day++;
if (Day <8)
Day = 1;
setDay(Day);
}
Why setting day to one if it is lower than 8? You should use:
if (Day >8) {
Day = 1;
}
"The current day and the next day values are not correctly printing."
nextDay() has no print statement
public void nextDay()
{
Day++;
if (Day <8)
Day = 1;
setDay(Day);
}
And you actually need to call setDay() before Days has a value;
Day myDay = new Day();
myDay.setDay(1);
myDay.printDay();
And the fact that you have a class Day, and int Day and String Days is totally confusing for anyone who is reading your code. I'm surprised you're even able to follow it. Consider Java naming convention: variable names start with lower case.
Your code is full of problems, problems that stem from a deep non-understanding of Java.
For example, look at this:
public Day setNameDay(String Day) {
Day = Days;
return this;
}
It is not only the naming that is bad.
What do you think this method actually does?
I seriuously suggest you take the course again, and pay attention when topics like parameter passing etc. come up.
You can use the Calendar API with Date.
try below code.
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class CameoWebServiceClient {
public enum day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
public static void main(String[] args) {
// read day
int lDay = 5;
SimpleDateFormat date_format = new SimpleDateFormat("yyyyMMdd");
System.out.println(date_format.format(new Date()));
Calendar cal1 = date_format.getCalendar();
cal1.set(Calendar.DAY_OF_WEEK, lDay);
cal1 = setDay(cal1, lDay);
System.out.println(""+curDay(cal1));
System.out.println(""+nextDay(cal1));
System.out.println(""+prevDay(cal1));
}
public static Calendar setDay(Calendar cal1, int lDay) {
cal1.set(Calendar.DAY_OF_WEEK, (lDay));
return cal1;
}
public static String curDay(Calendar cal1) {
int i = cal1.get(Calendar.DAY_OF_WEEK)-1 ;
i = i < 0 ? 6:i;
return day.values()[i]+"";
}
public static String nextDay(Calendar cal1) {
int i = cal1.get(Calendar.DAY_OF_WEEK);
i = i > 6 ? 0:i;
return day.values()[i]+"";
}
public static String prevDay(Calendar cal1) {
int i = cal1.get(Calendar.DAY_OF_WEEK)-2;
i = i < 0 ? 6:i;
return day.values()[i]+"";
}
}
Try updating your method as:
public String returnPreviousDay() {
return days[(index + days.length - 1) % days.length];
}
Here, instead of moving backwards to 0 index, I moved it forward by n - 1 days to get the previous day.
You are trying to re-invent something that already exists and it is making your life harder.
Simply store the data in a Calendar object. Use dateAdd to add and remove days, read the result.
import java.util.*;
import java.lang.*;
import java.io.*;
class Main {
public static void main(String[] args) {
Day day = new Day();
day.setDay(2);
day.printDay();
day.returnDay1(13);
day.returnDay();
day.nextDay();
day.previousDay(); }}
class Day {
int setday = 0;
int returnday=0;
String x,y;
static String[] days = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
void printDay() {
if(setday < 7) {
System.out.println("The Day is " + days[setday]); }
else
System.out.println("Invalid set of day"); }
void setDay(int dayset) {
setday = dayset; }
void returnDay1(int returnday1) {
returnday = returnday1; }
void returnDay() {
if(setday < 7) {
x = days[(setday + returnday )% days.length];
System.out.println("Return Day after "+String.valueOf(returnday)+ " days is " + x); }
else
System.out.println(" "); }
void nextDay() {
if(setday < 7) {
y = days[(setday + returnday + 1 )% days.length];
System.out.println("Next Day is " + y); }
else
System.out.println(" "); }
void previousDay() {
if(setday < 7) {
y = days[(setday + returnday - 1 )% days.length];
System.out.println("Previous Day is " + y); }
else
System.out.println(" "); } }

Regex date format validation on Java

I'm just wondering if there is a way (maybe with regex) to validate that an input on a Java desktop app is exactly a string formatted as: "YYYY-MM-DD".
Use the following regular expression:
^\d{4}-\d{2}-\d{2}$
as in
if (str.matches("\\d{4}-\\d{2}-\\d{2}")) {
...
}
With the matches method, the anchors ^ and $ (beginning and end of string, respectively) are present implicitly.
The pattern above checks conformance with the general “shape” of a date, but it will accept more invalid than valid dates. You may be surprised to learn that checking for valid dates — including leap years! — is possible using a regular expression, but not advisable. Borrowing from an answer elsewhere by Kuldeep, we can all find amusement and admiration for persistence in
((18|19|20)[0-9]{2}[\-.](0[13578]|1[02])[\-.](0[1-9]|[12][0-9]|3[01]))|(18|19|20)[0-9]{2}[\-.](0[469]|11)[\-.](0[1-9]|[12][0-9]|30)|(18|19|20)[0-9]{2}[\-.](02)[\-.](0[1-9]|1[0-9]|2[0-8])|(((18|19|20)(04|08|[2468][048]|[13579][26]))|2000)[\-.](02)[\-.]29
In a production context, your colleagues will appreciate a more straightforward implementation. Remember, the first rule of optimization is Don’t!
You need more than a regex, for example "9999-99-00" isn't a valid date. There's a SimpleDateFormat class that's built to do this. More heavyweight, but more comprehensive.
e.g.
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
boolean isValidDate(string input) {
try {
format.parse(input);
return true;
}
catch(ParseException e){
return false;
}
}
Unfortunately, SimpleDateFormat is both heavyweight and not thread-safe.
Putting it all together:
REGEX doesn't validate values (like "2010-19-19")
SimpleDateFormat does not check format ("2010-1-2", "1-0002-003" are accepted)
it's necessary to use both to validate format and value:
public static boolean isValid(String text) {
if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d"))
return false;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
try {
df.parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
A ThreadLocal can be used to avoid the creation of a new SimpleDateFormat for each call.
It is needed in a multithread context since the SimpleDateFormat is not thread safe:
private static final ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() {
#Override
protected SimpleDateFormat initialValue() {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
df.setLenient(false);
System.out.println("created");
return df;
}
};
public static boolean isValid(String text) {
if (text == null || !text.matches("\\d{4}-[01]\\d-[0-3]\\d"))
return false;
try {
format.get().parse(text);
return true;
} catch (ParseException ex) {
return false;
}
}
(same can be done for a Matcher, that also is not thread safe)
This will do it regex: "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$"
This will take care of valid formats and valid dates. It will not validate the correct days of the month i.e. leap year.
String regex = "^((19|20)\\d\\d)-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])$";
Assert.assertTrue("Date: matched.", Pattern.matches(regex, "2011-1-1"));
Assert.assertFalse("Date (month): not matched.", Pattern.matches(regex, "2011-13-1"));
Good luck!
I would go with a simple regex which will check that days doesn't have more than 31 days and months no more than 12. Something like:
(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\\d\\d)
This is the format "dd-MM-yyyy". You can tweak it to your needs (for example take off the ? to make the leading 0 required - now its optional), and then use a custom logic to cut down to the specific rules like leap years February number of days case, and other months number of days cases. See the DateChecker code below.
I am choosing this approach since I tested that this is the best one when performance is taken into account. I checked this (1st) approach versus 2nd approach of validating a date against a regex that takes care of the other use cases, and 3rd approach of using the same simple regex above in combination with SimpleDateFormat.parse(date).
The 1st approach was 4 times faster than the 2nd approach, and 8 times faster than the 3rd approach. See the self contained date checker and performance tester main class at the bottom.
One thing that I left unchecked is the joda time approach(s). (The more efficient date/time library).
Date checker code:
class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* #param date the date that needs to be checked.
* #return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
Date checking/testing and performance testing:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Tester {
private static final String[] validDateStrings = new String[]{
"1-1-2000", //leading 0s for day and month optional
"01-1-2000", //leading 0 for month only optional
"1-01-2000", //leading 0 for day only optional
"01-01-1800", //first accepted date
"31-12-2199", //last accepted date
"31-01-2000", //January has 31 days
"31-03-2000", //March has 31 days
"31-05-2000", //May has 31 days
"31-07-2000", //July has 31 days
"31-08-2000", //August has 31 days
"31-10-2000", //October has 31 days
"31-12-2000", //December has 31 days
"30-04-2000", //April has 30 days
"30-06-2000", //June has 30 days
"30-09-2000", //September has 30 days
"30-11-2000", //November has 30 days
};
private static final String[] invalidDateStrings = new String[]{
"00-01-2000", //there is no 0-th day
"01-00-2000", //there is no 0-th month
"31-12-1799", //out of lower boundary date
"01-01-2200", //out of high boundary date
"32-01-2000", //January doesn't have 32 days
"32-03-2000", //March doesn't have 32 days
"32-05-2000", //May doesn't have 32 days
"32-07-2000", //July doesn't have 32 days
"32-08-2000", //August doesn't have 32 days
"32-10-2000", //October doesn't have 32 days
"32-12-2000", //December doesn't have 32 days
"31-04-2000", //April doesn't have 31 days
"31-06-2000", //June doesn't have 31 days
"31-09-2000", //September doesn't have 31 days
"31-11-2000", //November doesn't have 31 days
"001-02-2000", //SimpleDateFormat valid date (day with leading 0s) even with lenient set to false
"1-0002-2000", //SimpleDateFormat valid date (month with leading 0s) even with lenient set to false
"01-02-0003", //SimpleDateFormat valid date (year with leading 0s) even with lenient set to false
"01.01-2000", //. invalid separator between day and month
"01-01.2000", //. invalid separator between month and year
"01/01-2000", /// invalid separator between day and month
"01-01/2000", /// invalid separator between month and year
"01_01-2000", //_ invalid separator between day and month
"01-01_2000", //_ invalid separator between month and year
"01-01-2000-12345", //only whole string should be matched
"01-13-2000", //month bigger than 13
};
/**
* These constants will be used to generate the valid and invalid boundary dates for the leap years. (For no leap year, Feb. 28 valid and Feb. 29 invalid; for a leap year Feb. 29 valid and Feb. 30 invalid)
*/
private static final int LEAP_STEP = 4;
private static final int YEAR_START = 1800;
private static final int YEAR_END = 2199;
/**
* This date regex will find matches for valid dates between 1800 and 2199 in the format of "dd-MM-yyyy".
* The leading 0 is optional.
*/
private static final String DATE_REGEX = "((0?[1-9]|[12][0-9]|3[01])-(0?[13578]|1[02])-(18|19|20|21)[0-9]{2})|((0?[1-9]|[12][0-9]|30)-(0?[469]|11)-(18|19|20|21)[0-9]{2})|((0?[1-9]|1[0-9]|2[0-8])-(0?2)-(18|19|20|21)[0-9]{2})|(29-(0?2)-(((18|19|20|21)(04|08|[2468][048]|[13579][26]))|2000))";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
* Keep in mind that String.matches tries to match only the whole string.
*/
private static final String DATE_REGEX_ONLY_WHOLE_STRING = "^" + DATE_REGEX + "$";
/**
* The simple regex (without checking for 31 day months and leap years):
*/
private static final String DATE_REGEX_SIMPLE = "(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-((18|19|20|21)\\d\\d)";
/**
* This date regex is similar to the first one, but with the difference of matching only the whole string. So "01-01-2000-12345" won't pass with a match.
*/
private static final String DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING = "^" + DATE_REGEX_SIMPLE + "$";
private static final SimpleDateFormat SDF = new SimpleDateFormat("dd-MM-yyyy");
static {
SDF.setLenient(false);
}
private static final DateChecker dateValidatorSimple = new DateChecker(DATE_REGEX_SIMPLE);
private static final DateChecker dateValidatorSimpleOnlyWholeString = new DateChecker(DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
/**
* #param args
*/
public static void main(String[] args) {
DateTimeStatistics dateTimeStatistics = new DateTimeStatistics();
boolean shouldMatch = true;
for (int i = 0; i < validDateStrings.length; i++) {
String validDate = validDateStrings[i];
matchAssertAndPopulateTimes(
dateTimeStatistics,
shouldMatch, validDate);
}
shouldMatch = false;
for (int i = 0; i < invalidDateStrings.length; i++) {
String invalidDate = invalidDateStrings[i];
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, invalidDate);
}
for (int year = YEAR_START; year < (YEAR_END + 1); year++) {
FebruaryBoundaryDates februaryBoundaryDates = createValidAndInvalidFebruaryBoundaryDateStringsFromYear(year);
shouldMatch = true;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getValidFebruaryBoundaryDateString());
shouldMatch = false;
matchAssertAndPopulateTimes(dateTimeStatistics,
shouldMatch, februaryBoundaryDates.getInvalidFebruaryBoundaryDateString());
}
dateTimeStatistics.calculateAvarageTimesAndPrint();
}
private static void matchAssertAndPopulateTimes(
DateTimeStatistics dateTimeStatistics,
boolean shouldMatch, String date) {
dateTimeStatistics.addDate(date);
matchAndPopulateTimeToMatch(date, DATE_REGEX, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegex());
matchAndPopulateTimeToMatch(date, DATE_REGEX_ONLY_WHOLE_STRING, shouldMatch, dateTimeStatistics.getTimesTakenWithDateRegexOnlyWholeString());
boolean matchesSimpleDateFormat = matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(date, dateTimeStatistics.getTimesTakenWithSimpleDateFormatParse());
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimple(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE);
matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
dateTimeStatistics.getTimesTakenWithDateRegexSimpleOnlyWholeString(), shouldMatch,
date, matchesSimpleDateFormat, DATE_REGEX_SIMPLE_ONLY_WHOLE_STRING);
matchAndPopulateTimeToMatch(date, dateValidatorSimple, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimple());
matchAndPopulateTimeToMatch(date, dateValidatorSimpleOnlyWholeString, shouldMatch, dateTimeStatistics.getTimesTakenWithdateValidatorSimpleOnlyWholeString());
}
private static void matchAndPopulateTimeToMatchAndReturnMatchesAndCheck(
List<Long> times,
boolean shouldMatch, String date, boolean matchesSimpleDateFormat, String regex) {
boolean matchesFromRegex = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !((matchesSimpleDateFormat && matchesFromRegex) ^ shouldMatch) : "Parsing with SimpleDateFormat and date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, String regex, boolean shouldMatch, List<Long> times) {
boolean matches = matchAndPopulateTimeToMatchAndReturnMatches(date, regex, times);
assert !(matches ^ shouldMatch) : "date:" + date + "\nregex:" + regex + "\nshouldMatch:" + shouldMatch;
}
private static void matchAndPopulateTimeToMatch(String date, DateChecker dateValidator, boolean shouldMatch, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = dateValidator.check(date);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
assert !(matches ^ shouldMatch) : "date:" + date + "\ndateValidator with regex:" + dateValidator.getRegex() + "\nshouldMatch:" + shouldMatch;
}
private static boolean matchAndPopulateTimeToMatchAndReturnMatches(String date, String regex, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches;
timestampStart = System.nanoTime();
matches = date.matches(regex);
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
return matches;
}
private static boolean matchWithSimpleDateFormatAndPopulateTimeToMatchAndReturnMatches(String date, List<Long> times) {
long timestampStart;
long timestampEnd;
boolean matches = true;
timestampStart = System.nanoTime();
try {
SDF.parse(date);
} catch (ParseException e) {
matches = false;
} finally {
timestampEnd = System.nanoTime();
times.add(timestampEnd - timestampStart);
}
return matches;
}
private static FebruaryBoundaryDates createValidAndInvalidFebruaryBoundaryDateStringsFromYear(int year) {
FebruaryBoundaryDates februaryBoundaryDates;
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(29, year),
createFebruaryDateFromDayAndYear(30, year)
);
} else {
februaryBoundaryDates = new FebruaryBoundaryDates(
createFebruaryDateFromDayAndYear(28, year),
createFebruaryDateFromDayAndYear(29, year)
);
}
return februaryBoundaryDates;
}
private static String createFebruaryDateFromDayAndYear(int day, int year) {
return String.format("%d-02-%d", day, year);
}
static class FebruaryBoundaryDates {
private String validFebruaryBoundaryDateString;
String invalidFebruaryBoundaryDateString;
public FebruaryBoundaryDates(String validFebruaryBoundaryDateString,
String invalidFebruaryBoundaryDateString) {
super();
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
public String getValidFebruaryBoundaryDateString() {
return validFebruaryBoundaryDateString;
}
public void setValidFebruaryBoundaryDateString(
String validFebruaryBoundaryDateString) {
this.validFebruaryBoundaryDateString = validFebruaryBoundaryDateString;
}
public String getInvalidFebruaryBoundaryDateString() {
return invalidFebruaryBoundaryDateString;
}
public void setInvalidFebruaryBoundaryDateString(
String invalidFebruaryBoundaryDateString) {
this.invalidFebruaryBoundaryDateString = invalidFebruaryBoundaryDateString;
}
}
static class DateTimeStatistics {
private List<String> dates = new ArrayList<String>();
private List<Long> timesTakenWithDateRegex = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimple = new ArrayList<Long>();
private List<Long> timesTakenWithDateRegexSimpleOnlyWholeString = new ArrayList<Long>();
private List<Long> timesTakenWithSimpleDateFormatParse = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimple = new ArrayList<Long>();
private List<Long> timesTakenWithdateValidatorSimpleOnlyWholeString = new ArrayList<Long>();
public List<String> getDates() {
return dates;
}
public List<Long> getTimesTakenWithDateRegex() {
return timesTakenWithDateRegex;
}
public List<Long> getTimesTakenWithDateRegexOnlyWholeString() {
return timesTakenWithDateRegexOnlyWholeString;
}
public List<Long> getTimesTakenWithDateRegexSimple() {
return timesTakenWithDateRegexSimple;
}
public List<Long> getTimesTakenWithDateRegexSimpleOnlyWholeString() {
return timesTakenWithDateRegexSimpleOnlyWholeString;
}
public List<Long> getTimesTakenWithSimpleDateFormatParse() {
return timesTakenWithSimpleDateFormatParse;
}
public List<Long> getTimesTakenWithdateValidatorSimple() {
return timesTakenWithdateValidatorSimple;
}
public List<Long> getTimesTakenWithdateValidatorSimpleOnlyWholeString() {
return timesTakenWithdateValidatorSimpleOnlyWholeString;
}
public void addDate(String date) {
dates.add(date);
}
public void addTimesTakenWithDateRegex(long time) {
timesTakenWithDateRegex.add(time);
}
public void addTimesTakenWithDateRegexOnlyWholeString(long time) {
timesTakenWithDateRegexOnlyWholeString.add(time);
}
public void addTimesTakenWithDateRegexSimple(long time) {
timesTakenWithDateRegexSimple.add(time);
}
public void addTimesTakenWithDateRegexSimpleOnlyWholeString(long time) {
timesTakenWithDateRegexSimpleOnlyWholeString.add(time);
}
public void addTimesTakenWithSimpleDateFormatParse(long time) {
timesTakenWithSimpleDateFormatParse.add(time);
}
public void addTimesTakenWithdateValidatorSimple(long time) {
timesTakenWithdateValidatorSimple.add(time);
}
public void addTimesTakenWithdateValidatorSimpleOnlyWholeString(long time) {
timesTakenWithdateValidatorSimpleOnlyWholeString.add(time);
}
private void calculateAvarageTimesAndPrint() {
long[] sumOfTimes = new long[7];
int timesSize = timesTakenWithDateRegex.size();
for (int i = 0; i < timesSize; i++) {
sumOfTimes[0] += timesTakenWithDateRegex.get(i);
sumOfTimes[1] += timesTakenWithDateRegexOnlyWholeString.get(i);
sumOfTimes[2] += timesTakenWithDateRegexSimple.get(i);
sumOfTimes[3] += timesTakenWithDateRegexSimpleOnlyWholeString.get(i);
sumOfTimes[4] += timesTakenWithSimpleDateFormatParse.get(i);
sumOfTimes[5] += timesTakenWithdateValidatorSimple.get(i);
sumOfTimes[6] += timesTakenWithdateValidatorSimpleOnlyWholeString.get(i);
}
System.out.println("AVG from timesTakenWithDateRegex (in nanoseconds):" + (double) sumOfTimes[0] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[1] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple (in nanoseconds):" + (double) sumOfTimes[2] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[3] / timesSize);
System.out.println("AVG from timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) sumOfTimes[4] / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimple + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[2] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithDateRegexSimpleOnlyWholeString + timesTakenWithSimpleDateFormatParse (in nanoseconds):" + (double) (sumOfTimes[3] + sumOfTimes[4]) / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimple (in nanoseconds):" + (double) sumOfTimes[5] / timesSize);
System.out.println("AVG from timesTakenWithdateValidatorSimpleOnlyWholeString (in nanoseconds):" + (double) sumOfTimes[6] / timesSize);
}
}
static class DateChecker {
private Matcher matcher;
private Pattern pattern;
public DateChecker(String regex) {
pattern = Pattern.compile(regex);
}
/**
* Checks if the date format is a valid.
* Uses the regex pattern to match the date first.
* Than additionally checks are performed on the boundaries of the days taken the month into account (leap years are covered).
*
* #param date the date that needs to be checked.
* #return if the date is of an valid format or not.
*/
public boolean check(final String date) {
matcher = pattern.matcher(date);
if (matcher.matches()) {
matcher.reset();
if (matcher.find()) {
int day = Integer.parseInt(matcher.group(1));
int month = Integer.parseInt(matcher.group(2));
int year = Integer.parseInt(matcher.group(3));
switch (month) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12: return day < 32;
case 4:
case 6:
case 9:
case 11: return day < 31;
case 2:
int modulo100 = year % 100;
//http://science.howstuffworks.com/science-vs-myth/everyday-myths/question50.htm
if ((modulo100 == 0 && year % 400 == 0) || (modulo100 != 0 && year % LEAP_STEP == 0)) {
//its a leap year
return day < 30;
} else {
return day < 29;
}
default:
break;
}
}
}
return false;
}
public String getRegex() {
return pattern.pattern();
}
}
}
Some useful notes:
- to enable the assertions (assert checks) you need to use -ea argument when running the tester. (In eclipse this is done by editing the Run/Debug configuration -> Arguments tab -> VM Arguments -> insert "-ea"
- the regex above is bounded to years 1800 to 2199
- you don't need to use ^ at the beginning and $ at the end to match only the whole date string. The String.matches takes care of that.
- make sure u check the valid and invalid cases and change them according the rules that you have.
- the "only whole string" version of each regex gives the same speed as the "normal" version (the one without ^ and $). If you see performance differences this is because java "gets used" to processing the same instructions so the time lowers. If you switch the lines where the "normal" and the "only whole string" version execute, you will see this proven.
Hope this helps someone!
Cheers,
Despot
java.time
The proper (and easy) way to do date/time validation using Java 8+ is to use the java.time.format.DateTimeFormatter class. Using a regex for validation isn't really ideal for dates. For the example case in this question:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate date = formatter.parse(text, LocalDate::from);
} catch (DateTimeParseException e) {
// Thrown if text could not be parsed in the specified format
}
This code will parse the text, validate that it is a valid date, and also return the date as a LocalDate object. Note that the DateTimeFormatter class has a number of static predefined date formats matching ISO standards if your use case matches any of them.
The following regex will accept YYYY-MM-DD (within the range 1600-2999 year) formatted dates taking into consideration leap years:
^((?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:(?:0[13578]|1[02])(-)31)|((0[1,3-9]|1[0-2])(-)(29|30))))$|^(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(-)02(-)29)$|^(?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:0[1-9])|(?:1[0-2]))(-)(?:0[1-9]|1\d|2[0-8])$
Examples:
You can test it here.
Note: if you want to accept one digit as month or day you can use:
^((?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:(?:0?[13578]|1[02])(-)31)|((0?[1,3-9]|1[0-2])(-)(29|30))))$|^(?:(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00)))(-)0?2(-)29)$|^(?:(?:1[6-9]|2[0-9])\d{2})(-)(?:(?:0?[1-9])|(?:1[0-2]))(-)(?:0?[1-9]|1\d|2[0-8])$
I have created the above regex starting from this solution
The pattern yyyy-MM-dd is the default pattern used by LocalDate#parse. Therefore, all you need to do is to pass your date string to this method and check if it is successfully parsed or some exception has occurred.
Demo:
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
String[] arr = { "2022-11-29", "0000-00-00", "2022-1-2", "2022-02-29" };
for (String s : arr) {
try {
System.out.println("================================");
System.out.println(LocalDate.parse(s) + " is a valid date ");
} catch (DateTimeParseException e) {
System.out.println(e.getMessage());
// Recommended; so that the caller can handle it appropriately
// throw new IllegalArgumentException("Invalid date");
}
}
}
}
Output:
================================
2022-11-29 is a valid date
================================
Text '0000-00-00' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 0
================================
Text '2022-1-2' could not be parsed at index 5
================================
Text '2022-02-29' could not be parsed: Invalid date 'February 29' as '2022' is not a leap year
Some important notes:
java.time types follow ISO 8601 standards and therefore you do need to specify a DateTimeFormatter to parse a date-time string which is in ISO 8601 format.
The java.time API, released with Java-8 in March 2014, supplanted the error-prone legacy date-time API. Since then, it has been strongly recommended to use this modern date-time API. Learn more about the modern Date-Time API from Trail: Date Time.
Construct a SimpleDateFormat with the mask, and then call:
SimpleDateFormat.parse(String s, ParsePosition p)
For fine control, consider an InputVerifier using the SimpleDateFormat("YYYY-MM-dd") suggested by Steve B.
Below added code is working for me if you are using pattern
dd-MM-yyyy.
public boolean isValidDate(String date) {
boolean check;
String date1 = "^(0?[1-9]|[12][0-9]|3[01])-(0?[1-9]|1[012])-([12][0-9]{3})$";
check = date.matches(date1);
return check;
}
If you want a simple regex then it won't be accurate.
https://www.freeformatter.com/java-regex-tester.html#ad-output offers a tool to test your Java regex. Also, at the bottom you can find some suggested regexes for validating a date.
ISO date format (yyyy-mm-dd):
^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$
ISO date format (yyyy-mm-dd) with separators '-' or '/' or '.' or ' '. Forces usage of same separator accross date.
^[0-9]{4}([- /.])(((0[13578]|(10|12))\1(0[1-9]|[1-2][0-9]|3[0-1]))|(02\1(0[1-9]|[1-2][0-9]))|((0[469]|11)\1(0[1-9]|[1-2][0-9]|30)))$
United States date format (mm/dd/yyyy)
^(((0[13578]|(10|12))/(0[1-9]|[1-2][0-9]|3[0-1]))|(02/(0[1-9]|[1-2][0-9]))|((0[469]|11)/(0[1-9]|[1-2][0-9]|30)))/[0-9]{4}$
Hours and minutes, 24 hours format (HH:MM):
^(20|21|22|23|[01]\d|\d)((:[0-5]\d){1,2})$
Good luck

Categories