Dates in Java

Today I woke up around 05:00 AM. Later this morning my wife has a lab appointment at 08:15 AM. I decided to get up, cancel my 06:00 AM alarm, and set a new alarm for 07:15 AM. That should give us ample time to get ready and drive to the appointment. Will have breakfast when we get back because she needs to fast for the blood test.

Not sure what is going on with the Presidential Election 2020 results. As I have mentioned in a previous post, the entire process is open to fraud. Do not understand how in the USA we do not use a voter ID card. In addition we should have a secure system in which voters go to their designated polling sites and vote electronically. A paper receipt can be used by voters to make sure the ballot is counted and has not been tampered with. Election Day should be the only day one can vote. If you miss it because it is not important to you, so you can wait four years and plan accordingly.

I have received messages from friends living in different countries (i.e., Australia, Canada, England, Israel, and Peru to mention just a few) around the world. They are all amazed how little technical ability and lack of morals exists among Americans. It seems that third world countries can do better handling elections and controlling electoral fraud.

OK, I had to release some steam. Not sure what is happening in our country. Hopefully the issues will be addressed and we will never get into the situation we are on.

We own a parking ramp (I wish).

Our customers, based on different factors, which are outside of the
scope of this problem, get assigned different hourly rates.

The task, if you wish to accept, is to compute the gross income generated by all customers
that parked their vehicles in the parking ramp.

Given a function that specifies a month, a list of parking spaces, and a list of
customers, return the gross income.

The format for the gross income should contain no more than two (s) decimals.

The task is to generate the gross income for our parking ramp. The result should have no more than two decimals.

	/**
     * Determine gross revenue for the parking ramp for the remainder of the current month.
     * The value should be returned with no more than two decimal values.
     */
	static double grossRevenue(Month month, ArrayList<ParkingSpace> spaces, ArrayList<Customer> customers) {
	}

The grossRevenue() function represents the function you need to implement. As we will see later on, the month is not a necessary argument given that the data generated by the test scaffolding is only for the remaining days in the current month (in the future). Because of this, the results will be different based on the current date. In addition, some values are randomly generated. That also makes results be different on each invocation.

I was planning on adding in the test scaffolding, a mechanism to compute the result and then check the value returned by the grossRevenue() method. I will leave it as extra points if you are interested. I would do it but I just ran out of time.

main <<< localDate: 2020-11-05
grossRevenue <<<          month: NOVEMBER
grossRevenue <<<    spaces.size: 104
grossRevenue <<< customers.size: 4
main <<< gross: 5199.78

It seems that the test scaffolding displays the current date. In our only example, it is 2020-11-15. That date will change to the date on which you run the software.

The grossRevenue() function displays some information about the arguments. It is being called with the month of NOVEMBER which makes sense due to the date in which the code was executed. It seems we have rented 104 spaces and have 4 customers in this pass. Such numbers are randomly generated based on some constants which you can alter in the source code.

As the title for the post indicates, we are using Java for this problem. I am using the VSCode IDE running on a Windows 10 machine. You can experiment with the code using different IDEs and platforms. The test scaffolding should work fine.

/**
 * Parking lot customer.
 */
class Customer {

    // **** member(s) ****
    public int      customerID;
    public int      spaceID;
    public double   rate;

    // **** methods ****
    public Customer(int customerID, int spaceID, double rate) {
        this.customerID = customerID;
        this.spaceID    = spaceID;
        this.rate       = rate;
    }

    @Override
    public String toString() {
        String s = String.format("%.2f", rate);
        return "[customerID: " + customerID + " spaceID: " + spaceID + " rate: " + s + "]";
    }
}

The Customer class represents a parking lot customer. I planned to make it more extensive, but there is not much you can do in two hours.

/**
 * Parking space in parking lot.
 */
class ParkingSpace {

    // **** member(s) ****
    public int              spaceID;
    public int              customerID;
    public LocalDateTime    arrival;
    public LocalDateTime    departure;

    // **** methods ****
    public ParkingSpace(int spaceID, int customerID) {
        this.spaceID    = spaceID;
        this.customerID = customerID;
    }

    public ParkingSpace(int spaceID, int customerID, LocalDateTime arrival, LocalDateTime departure) {
        this.spaceID    = spaceID;
        this.customerID = customerID;
        this.arrival    = arrival;
        this.departure  = departure;
    }   

    @Override
    public String toString() {
        return "[spaceID: " + spaceID + " customerID: " + customerID 
                + " arrival: " + arrival + " departure: " + departure + "]";
    }
}

The ParkingSpace class is used to represent two things (not a good idea). One is a parking lot space, and the other a parking lot ticket. I fully realize this is not the proper way to do it, but I was running out of time. I should have created a ParkingSpace and a ParkingTicket classes. Please feel free to experiment enhancing this code to work with at least three classes.

Please take a moment to understand these two classes and determine how they can be used to generate the desired results.

    // **** constants ****
    final static int MIN_CUSTOMERS = 3;
    final static int MAX_CUSTOMERS = 5;

    final static int MIN_SPOT_ID = 3;
    final static int MAX_SPOT_ID = 5;

    final static double MIN_RATE = 5.00;
    final static double MAX_RATE = 7.00;

These are constants that are used to configure the test scaffolding. Note that you are able to change parameters to generate more or less customers and parking spots. Note that the current version of the test scaffolding uses the same ID for the customer and the assigned parking stall. With additional time we should have made both members completely independent.

    /**
     * Test scaffolding.
     */
    static public void main(String[] args) {

        // **** get the current date  ****
        LocalDate localDate = LocalDate.now();
        int year            = localDate.getYear();
        int month           = localDate.getMonthValue();
        Month m             = localDate.getMonth();

        // **** display local date ****
        System.out.println("main <<< localDate: " + localDate.toString());

        // **** generate a list of customers ****
        ArrayList<Customer> customers = new ArrayList<Customer>();

        // **** generate a number of customers ****
        Random rand = new Random();
        int numOfCust = rand.nextInt(MAX_CUSTOMERS - MIN_CUSTOMERS) + MIN_CUSTOMERS;

        // **** populate list of customers ****
        int spaceID = MIN_SPOT_ID - 1;
        for (int id = 1; id <= numOfCust; id++) {

            // **** select a spot ID ****
            spaceID += 1;
            if (spaceID > MAX_SPOT_ID)
                spaceID = MIN_SPOT_ID;

            // **** compute an hourly rate ****
            Double rate = rand.nextInt((int)(MAX_RATE - MIN_RATE)) + MIN_RATE;

            // **** create a customer ****
            Customer customer = new Customer(id, spaceID, rate);

            // **** add customer to the list ****
            customers.add(customer);
        }

        // **** display customer list ****
        // customers.forEach(c -> System.out.println("main <<< " + c));
        // System.out.println();

        // **** generate list of parking spaces ****
        ArrayList<ParkingSpace> parkingSpaces = new ArrayList<ParkingSpace>();

        // **** populate parking spaces for the specified month ****
        LocalDateTime arrival   = LocalDateTime.now();
        year                    = arrival.getYear();
        month                   = arrival.getMonthValue();
        int dayOfMonth          = arrival.getDayOfMonth();
        int hour                = arrival.getHour();
        int minute              = arrival.getMinute();

        LocalDateTime departure = LocalDateTime.now();

        // **** loop once per day in the remaining days of the month ****
        int currentMonth = arrival.getMonthValue();
        while (arrival.getMonthValue() == currentMonth) {

            // **** loop once per customer ****
            for (int custID = 1; custID <= numOfCust; custID++) {

                // **** generate a random arrival time ****
                hour    = 6 + rand.nextInt(6);
                minute  = rand.nextInt(60);

                // **** ****
                arrival = LocalDateTime.of(year, month, dayOfMonth, hour, minute);

                // **** generate a random departure time ****
                hour    = 12 + rand.nextInt(12);
                minute  = rand.nextInt(60);               

                // **** ****
                departure = LocalDateTime.of(year, month, dayOfMonth, hour, minute);

                // **** customers are assigned a parking spot ****
                spaceID = custID;

                // **** ****
                ParkingSpace parkingSpace = new ParkingSpace(spaceID, custID, arrival, departure);

                // **** ****
                parkingSpaces.add(parkingSpace);
            }

            // **** increment day ****
            arrival = arrival.plusDays(1);
            dayOfMonth = arrival.getDayOfMonth();
        }

        // **** display parking spaces list ****
        // parkingSpaces.forEach( ps -> System.out.println("main <<< " + ps));
        // System.out.println();

        // **** compute the gross income for the parking lot for the specified month ****
        double gross = grossRevenue(m, parkingSpaces, customers);

        // **** display the gross income ****
        System.out.println("main <<< gross: " + gross);
    }

The test scaffolding generates the data that we will use to run our target method grossRevenue() which is invoked in the lines towards the end of the test scaffolding. Note that we invoke the function, collect the result and then display the result. These two lines can easily be combines into one.

    /**
     * Determine gross revenue for the parking ramp for the remainder of the current month.
     * The value should be returned with no more than two decimal values.
     */
    static double grossRevenue(Month month, ArrayList<ParkingSpace> spaces, ArrayList<Customer> customers) {
       
        // ???? ????
        System.out.println("grossRevenue <<<          month: " + month.toString());
        System.out.println("grossRevenue <<<    spaces.size: " + spaces.size());
        System.out.println("grossRevenue <<< customers.size: " + customers.size());

        // **** gross sales ****
        double gross = 0.0;

        // **** loop once per space space in spaces ****
        for (int i = 0; i < spaces.size(); i++) {

            // **** get the customer ID ****
            int customerID = spaces.get(i).customerID;

            // **** get the parking rate ****
            double rate = customers.get(customerID - 1).rate;

            // **** get arrival time ****
            LocalDateTime arrival = spaces.get(i).arrival;

            // **** get the departure time ****
            LocalDateTime departure = spaces.get(i).departure;

            // **** compute time difference in minutes ****
            int arrivalInMins   = arrival.getHour() * 60 + arrival.getMinute();
            int departureInMins = departure.getHour() * 60 + departure.getMinute();
            double diffMins     = departureInMins - arrivalInMins;

            // **** compute charge ****
            double charge = (rate / 60.0) * diffMins;

            // **** update gross revenue ****
            gross += charge;  
        }

        // **** ****
        String s = String.format("%.2f", gross);
        gross = Double.parseDouble(s);

        // **** return gross revenue ****
        return gross;
    }

We start by displaying some values. This was done to perform some sanity checks.

The gross variable is set to zero. We will use it to compute the result for our function.

We enter a loop in which we collect the parking rate for the customer, the arrival and departure times. With that data we compute the number of minutes the customer was in the ramp. We prorate the rate based on the time and compute the charge to the customer for parking. We just add the charge to the gross and proceed looping.

After we process all parking tickets we have computed the gross generated by our parking ramp. We just need to format the result with no more than two decimal digits. We then return the gross income.

While I was working on the problem I figured that we could specify that after some number of hours, we could stop increasing the charges on each stall. For example, customers had to pay a flat rate for the first 3 or 4 hours and the rest of the time that day would be free; so many ideas, so little time.

Hope you enjoyed solving this problem as much as I did. The entire code for this project can be found in my GitHub repository.

If you have comments or questions regarding this, or any other post in this blog, or if you would like for me to help out with any phase in the SDLC (Software Development Life Cycle) of a project associated with a product or service, please do not hesitate and leave me a note below. If you prefer, send me a private e-mail message. I will reply as soon as possible.

Keep on reading and experimenting. It is the best way to learn, become proficient, refresh your knowledge and enhance your developer toolset!

One last thing, many thanks to all 3,695 subscribers to this blog!!!

Keep safe during the COVID-19 pandemic and help restart the world economy.

John

E-mail:  john.canessa@gmail.com

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.