-2

After keeping getting "warnings" of my question is not good enough I will try again and get every single thing added to this question.

I'm trying to make an application where I can calculate my salary (hourly paid). For this there is certain requirements since different time slots/days have different appendices.

My most important requirement, and the one I'm strugling with is to check if my workday has been between the timeslot of 01:00-06:00, if so how many hours. E.g. start Monday 23:00 end Tuesday 05:00 I have worked 06:00 hours and 04:00 hours has been between 01:00-06:00

This is my current code where I have tried with different types of if-statements to get the interval, but I can't get it to work in all situations (no matter what I type), this is because of the difficulty with it possibly is going from one day to another.

(My notes etc. are in danish, but this question is only regarding table 2,3 and 8 and is all in my tableChanged method)

import java.awt.*;
import java.awt.event.*;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Calendar;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.*;

public class TimeDifferenceCalculator222 extends JFrame {
    private JTextField dateField;
    private JTextField dateField1;
    private JTable table;
    private DefaultTableModel model;

    public TimeDifferenceCalculator222() {
        super("Time Difference Calculator");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Opret tekstfelt for dato
        dateField = new JTextField(10);
        dateField.setToolTipText("Indtast dato i formatet dd/MM/yyyy");

        // Opret tabel for datoer og start/slut-tidspunkter
        model = new DefaultTableModel() {
            @Override
            public boolean isCellEditable(int row, int column) {
                return column != 0;
            }
        };
        table = new JTable(model);
        model.addColumn("Dato");
        model.addColumn("Ugedag");
        model.addColumn("Starttidspunkt");
        model.addColumn("Sluttidspunkt");
        model.addColumn("Differencetid");
        model.addColumn("Branchetillæg");
        model.addColumn("VC1 tillæg");
        model.addColumn("Nattetillæg");
        model.addColumn("Nattetimer (01:00-06:00)");
        model.addColumn("Weekendtillæg (lørdag)");
        model.addColumn("Weekend / Helligdage");

        // Tilføj 31 rækker til tabellen
        LocalDate startDate = LocalDate.now();
        for (int i = 0; i < 31; i++) {
            model.addRow(new Object[] { startDate.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")), "", "", "" });
            startDate = startDate.plusDays(1);
        }

        // Tilføj lytter til dato-feltet
        dateField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    LocalDate date = LocalDate.parse(dateField.getText(), DateTimeFormatter.ofPattern("dd/MM/yyyy"));
                    LocalDate endDate = date.plusDays(30);
                    int row = 0;
                    for (LocalDate d = date; !d.isAfter(endDate); d = d.plusDays(1)) {
                        model.setValueAt(d.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")), row, 0);
                        Calendar calendar = Calendar.getInstance();
                        calendar.set(d.getYear(), d.getMonthValue() - 1, d.getDayOfMonth());
                        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
                        String dayOfWeekString;
                        switch (dayOfWeek) {
                            case Calendar.SUNDAY:
                                dayOfWeekString = "Søndag";
                                break;
                            case Calendar.MONDAY:
                                dayOfWeekString = "Mandag";
                                break;
                            case Calendar.TUESDAY:
                                dayOfWeekString = "Tirsdag";
                                break;
                            case Calendar.WEDNESDAY:
                                dayOfWeekString = "Onsdag";
                                break;
                            case Calendar.THURSDAY:
                                dayOfWeekString = "Torsdag";
                                break;
                            case Calendar.FRIDAY:
                                dayOfWeekString = "Fredag";
                                break;
                            case Calendar.SATURDAY:
                                dayOfWeekString = "Lørdag";
                                break;
                            default:
                                dayOfWeekString = "";
                                break;
                        }
                        model.setValueAt(dayOfWeekString, row, 1);
                        row++;
                    }
                } catch (DateTimeParseException ex) {
                    JOptionPane.showMessageDialog(TimeDifferenceCalculator222.this, "Ugyldig datoformat", "Fejl", JOptionPane.ERROR_MESSAGE);
                }
            }
        });

        // Tilføj lytter til tabelen
        table.getModel().addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                if (e.getType() == TableModelEvent.UPDATE && e.getColumn() >= 2 && e.getColumn() <= 3) {
                    int row = e.getFirstRow();
                    String startStr = (String) model.getValueAt(row, 2);
                    String endStr = (String) model.getValueAt(row, 3);
                    if (!startStr.isEmpty() && !endStr.isEmpty()) {
                        LocalTime start = LocalTime.parse(startStr, DateTimeFormatter.ofPattern("HH:mm"));
                        LocalTime end = LocalTime.parse(endStr, DateTimeFormatter.ofPattern("HH:mm"));
                        LocalDateTime startDateTime = LocalDateTime.of(LocalDate.parse((String) model.getValueAt(row, 0), DateTimeFormatter.ofPattern("dd/MM/yyyy")), start);
                        LocalDateTime endDateTime = LocalDateTime.of(LocalDate.parse((String) model.getValueAt(row, 0), DateTimeFormatter.ofPattern("dd/MM/yyyy")), end);
                        if (end.isBefore(start)) {
                            endDateTime = endDateTime.plusDays(1);
                        }
                        Duration duration = Duration.between(startDateTime, endDateTime);
                        long minutes = duration.toMinutes();
                        long hours = minutes / 60;
                        long remainingMinutes = minutes % 60;
                        String differenceTime = String.format("%02d:%02d", hours, remainingMinutes);
                        model.setValueAt(differenceTime, row, 4);

                        // Beregn antal timer mellem 01:00 og 06:00 og sæt i kolonne 8
                        LocalTime oneAM = LocalTime.of(1, 0);
                        LocalTime sixAM = LocalTime.of(6, 0);


                        if (start.isBefore(oneAM) && end.isAfter(oneAM) || start.isAfter(oneAM) && end.isBefore(sixAM) || start.isAfter(oneAM) && end.isAfter(sixAM)) {

                            LocalDateTime start1am = LocalDateTime.of(LocalDate.parse((String) model.getValueAt(row, 0), DateTimeFormatter.ofPattern("dd/MM/yyyy")), oneAM);
                            LocalDateTime end6am = LocalDateTime.of(LocalDate.parse((String) model.getValueAt(row, 0), DateTimeFormatter.ofPattern("dd/MM/yyyy")), sixAM);
                            if (start.isBefore(oneAM) && end.isBefore(sixAM)) {
                                start1am = LocalDateTime.from(oneAM);
                            }
                            if (start.isAfter(oneAM) && end.isBefore(sixAM)) {
                                start1am = LocalDateTime.from(startDateTime);
                                end6am = LocalDateTime.from(endDateTime);
                            }
                            if (end.isBefore(sixAM)) {
                                end6am = endDateTime;
                            }
                            Duration durationBetween1and6 = Duration.between(start1am, end6am);
                            long minutesBetween1and6 = durationBetween1and6.toMinutes();
                            long hoursBetween1and6 = minutesBetween1and6 / 60;
                            long remainingMinutesBetween1and6 = minutesBetween1and6 % 60;
                            String hoursMinutesBetween1and6 = String.format("%02d:%02d", hoursBetween1and6, remainingMinutesBetween1and6);
                            model.setValueAt(hoursMinutesBetween1and6, row, 8);

                        }

                    }
                }
            }
        });

        // Opret scroll-pane og tilføj tabelen til den
        JScrollPane scrollPane = new JScrollPane(table);

        // Tilføj komponenter til frame
        Container c = getContentPane();
        c.setLayout(new BorderLayout());
        c.add(dateField, BorderLayout.NORTH);
        c.add(scrollPane, BorderLayout.CENTER);

        // Vis frame
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        new TimeDifferenceCalculator222();
    }
}


DinoKing
  • 7
  • 3
  • 2
    what are your inputs and outputs – drum May 05 '23 at 17:18
  • Does this answer your question? [java timestamps calculate overlap duration with interval](https://stackoverflow.com/questions/51465913/java-timestamps-calculate-overlap-duration-with-interval) – Progman May 05 '23 at 17:23
  • `if (lt1.isAfter(lt2)) { System.out.printf("Time %s is after time %s%n", lt1, lt2);System.out.printf("Time %s is before time %s%n", lt1.atDate(LocalDate.now()), lt2.atDate(LocalDate.now()).plusDays(1));}` perhaps? – g00se May 05 '23 at 17:36
  • If you want any of this to work your timestamps must be _complete_ timestamps (i.e. `Instant`), with both date and time. Then calculating the interval is just a subtraction. – Jim Garrison May 05 '23 at 17:43
  • @drum - current output with my example of 23:00-05:00 is 28:00 – DinoKing May 05 '23 at 18:31
  • Your numbers do not make sense. What does "01:00-06:00" have to do with "workday starts at 23:00 and ends at 05:00"? You say "The result is 4 hours" but none of your examples are four hours long. Please take more care when drafting your Question. – Basil Bourque May 05 '23 at 19:17
  • 1
    @BasilBourque if you start at 23:00 and end at 05:00, exactly four of the hours worked are between 01:00 and 06:00 (from 01:00 to 05:00). – David Conrad May 05 '23 at 20:30
  • What types are `start` and `end`? If starting after 06:00, simply use midnight as start. – David Conrad May 05 '23 at 20:32
  • [mcve] please (mind the __M__!) - if you have problems using Date/Time api, there is no need to mix-in the ui (or the other way round :) And __do not__ use the old Date api. – kleopatra May 09 '23 at 09:43
  • @kleopatra - TY for your comment, unfortunately I can't make everyone happy and some want more, some want less of my code. My code does not that big, so I hope (and think) it is okay for now. What should I use instead of the "old date api" in your opinion then? – DinoKing May 10 '23 at 07:02

1 Answers1

2

LocalDateTime is ambiguous

The LocalDateTime class is the wrong class for your purpose. The LocalDateTime class purposely lacks any concept of time zone or offset from UTC, so it is inherently indefinite, ambiguous.

When recording a moment, a specific point on the timeline, use Instant, OffsetDateTime, or ZonedDateTime.

Use ZonedDateTime

For your purpose, use ZonedDateTime. This class accounts for temporal anomalies such as Daylight Saving Time (DST) that occur in time zones with rules changed by politicians.

ZoneId z = ZoneId.of( "Asia/Tokyo" );
ZonedDateTime start = ZonedDateTime.of( 2023 , 5 , 13 , 23 , 0 , 0 , 0 , z );
ZonedDateTime end = ZonedDateTime.of( 2023 , 5 , 14 , 5 , 0 , 0 , 0 , z );
Duration duration = Duration.between( start , end );

When run we get a result of 6 hours between those two moments.

start.toString() = 2023-05-13T23:00+09:00[Asia/Tokyo]
end.toString() = 2023-05-14T05:00+09:00[Asia/Tokyo]
duration.toString() = PT6H

Intersection of time spans

Thanks to David Conrad, I see now that you want to know how much of one span of time coincides with another.

This calculation is actually quite easy, if you add the ThreeTen-Extra library to your project. From that library, make use of the org.threeten.extra.Interval class. That class represents a span of time attached to the timeline. The beginning & ending of the span are moments as seen with an offset from UTC of zero hours-minutes-seconds. These moments are represented with the java.time.Instant class.

Get the interval of our worked time.

Interval worked = Interval.of( start.toInstant() , end.toInstant() );

Now get the target interval. We want 1 AM to 6 AM of the second day of work.

ZonedDateTime targetStart = ZonedDateTime.of( 2023 , 5 , 14 , 1 , 0 , 0 , 0 , z );
ZonedDateTime targetEnd = ZonedDateTime.of( 2023 , 5 , 14 , 6 , 0 , 0 , 0 , z );
Interval target = Interval.of( targetStart.toInstant() , targetEnd.toInstant() );

Notice that we used ZonedDateTime again, to represent the start and end of our target. We do this to account for politicians’ temporal anomalies. The span from 1 AM to 6 AM may or may not be five hours long. It might be four hours, or six hours, or four and a half hours, or any other length the politicians dream up.

Get the intersection of those two spans of time.

Interval intersection = worked.intersection( target );

Determine how long is that span of time.

Duration durationOfIntersection = intersection.toDuration();

When run, we see a result of four hours.

worked.toString() = 2023-05-13T14:00:00Z/2023-05-13T20:00:00Z
target.toString() = 2023-05-13T16:00:00Z/2023-05-13T21:00:00Z
intersection.toString() = 2023-05-13T16:00:00Z/2023-05-13T20:00:00Z
durationOfIntersection.toString() = PT4H

The output of the various toString methods is shown above. The formats of those are part of the ISO 8601 standard. The Z at the end is an abbreviation of +00:00, an offset of zero.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • TY for your comment, but this does not work for me. - I also think you are making it more complex, than needed, with adding timezones etc. I'm trying to make it a simple project of mine, so I understand every step and not put too many things in to complex it, at this point. I also need to have the output in xx:xx format – DinoKing May 09 '23 at 04:18
  • 1
    @DinoKing Good luck with that. Be sure your app is *not* running on [Oct 29, 2023 in Denmark](https://www.timeanddate.com/time/change/denmark). – Basil Bourque May 09 '23 at 06:35