⌛⌛ Päivämäärät

Aseta vastauksesi tiedostoon Dates.java kansiossa Round5/dates Git-tietovarastossasi.

Toteuta päivämäärien erotuksia laskeva apuluokka Dates, jolla on alla kuvatut ominaisuudet.

  • Sisäinen julkinen staattinen luokka DateDiff, joka tallettaa alkupäivämäärän start ja loppupäivämäärän end merkkijonoina sekä päivämäärien välisen erotuksen diff kokonaislukuna.

    • Julkiset jäsenfunktiot String getStart(), String getEnd() ja int getDiff(), jotka palauttavat edellämainitut kolme tietoa.

      • Alku- ja loppupäivämäärä palautetaan ISO-8601 -muodossa “vuosi-kuukausi-päivä” eli esim. päivämäärä 6.12.2021 esitettäisiin merkkijonona “2021-12-06”.

    • Kaikki rakentimet (tuskin tarvitset enempää kuin yhden) yksityisiä!

      • Efekti: DateDiff-olioita voi luoda vain luokan Dates puitteissa; Dates-luokalla on pääsy sisäisen luokkansa DateDiff yksityisiin jäseniin.

    • Julkinen jäsenfunktio String toString(), joka palauttaa muotoa “start --> end: diff days” tai “start --> end: 1 day” olevan merkkijonon, missä sekä alku- että loppupäivämäärä on esitetty muodossa “Weekday päivä.kuukausi.vuosi”. Edellä “Weekday”, koska se esitetään tässä tehtävässä englanniksi.

    • Esimerkiksi jos DateDiff-olion dd alkupäivämäärä olisi 1.1.1999 ja loppupäivämäärä 1.1.2001, niin:

      • dd.getStart() palauttaisi “1999-01-01”, dd.getEnd() palauttaisi “2001-01-01” ja dd.toString() palauttaisi “Friday 01.01.1999 --> Monday 01.01.2001: 731 days”.

      • Viikonpäivä esitetään yksinkertaisuuden vuoksi tässä aina englanniksi.

  • Julkinen staattinen funktio DateDiff[] dateDiffs(String ...dateStrs), joka palauttaa saamiensa päivämäärien väliset erotukset kuvaavat DateDiff-oliot taulukkona. Erotus lasketaan päivämäärien välisinä päivinä. Esimerkiksi peräkkäisten päivämäärien 3.5.2021 ja 4.5.2021 erotus on 1, ja keskenään samojen päivämäärien erotus on 0.

    • Tarkemmin ottaen funktio lajittelee saamansa päivämäärät aikajärjestykseen, ja palauttaa kutakin kahta järjestyksessä peräkkäistä päivämäärää kohden yhden DateDiff-olion. Tämä tulee selväksi tehtävän lopun esimerkeistä.

    • Muistutus: vaihtelevan määrän hyväksyvä parametri dataStrs → funktio saa parametrin dateStrs String-taulukkona.

    • Kukin taulukon dateStrs merkkijono saa esittää päivämäärän joko ISO-8601 -muodossa tai suomalaisessa muodossa “päivä.kuukausi.vuosi”. ISO-8601 -muodossa on oltava päivä ja kuukausi kahdella numerolla ja vuosi neljällä. Funktio hyväksyy suomalaisen päivämäärän, jos päivä ja kuukausi on esitetty yhdellä tai kahdella numerolla (saa olla sekaisinkin) ja vuosi neljällä. Päivämäärien tulee lisäksi olla laillisia; esimerkiksi oikeaa muotoa olevat merkkijonot “2001-02-29” tai “10.13.2004” hylättäisiin, koska ne esittävät laittomia päivämääriä.

      • Kukin väärin muotoiltu tai laitonta päivämäärää esittävä merkkijono dateStr sivuutetaan, ja sellaisten kohdalla tulostetaan viesti “The date dateStr is illegal!”.

      • Funktion tulee ensin käydä dateStrs läpi ja ottaa niistä talteen vain lailliset päivämäärät. Sen jälkeen lailliset päivämäärät lajitellaan ja kullekin peräkkäiselle parille muodostetaan niitä vastaava DateDiff-olio.

      • Vaatimus nelinumeroisesta vuodesta tarkoittaa, että vain välin 1000…9999 vuodet hyväksytään.

    • Huomaa, että DateDiff-oliot palautetaan tavallisessa taulukossa. Jos funktio saa vähemmän kuin 2 laillista päivämäärää (eli yhtään DateDiff-oliota ei voi järkevästi muodostaa), palautetaan tyhjä taulukko. Sellaisenkin voi luoda tavalliseen tapaan eli vaikkapa new DateDiff[0] tai käyttäen tyhjää alustusarvoa {}.

Muutama vinkki

Päivämäärän laillisuuden tarkistaminen: miten LocalDate.of käyttäytyy, jos päivämäärä on laiton?

Viikonpäivä? LocalDate-olio tietää esittämänsä päivämäärän viikonpäivän.

Kahden päivämäärän välisen erotuksen laskenta? Eräs kohtalaisen suoraviivainen vaihtoehto on funktio LocalDate.until.

Päivämäärien muotoilun (esim. englanninkieliset viikonpäivien nimet) voi toki toteuttaa itse. Mutta jos haluat hyödyntää Javan valmiita apuvälineitä päivämäärän esittämiseen, esim. luokka DateTimeFormatter yhdessä localen Locale.US kanssa voisi sopia tähän tehtävään.

Esimerkkitestejä

Automaattiset testit testaavat luokan Dates toteutustasi suunnilleen seuraavanlaisella testiohjelmalla:

public class DatesTest {
  public static void main(String args[]) {
    Dates.DateDiff[] diffArray = {};
    if(args.length == 2) {
      diffArray = Dates.dateDiffs(args[0], args[1]);
    }
    else {
      diffArray = Dates.dateDiffs(args);
    }
    for(Dates.DateDiff dd : diffArray) {
      System.out.format("start: %s end: %s diff: %d%n",
              dd.getStart(), dd.getEnd(), dd.getDiff());
      System.out.println("  " + dd);
    }
  }
}

Alla on kuvattu esimerkiksi kolme testiohjelman suoritusta odotettuine tuloksineen:

java DatesTest 1.1.2022 5.6.850

The date "5.6.850" is illegal!

java DatesTest 2022-05-31 1.1.2022

start: 2022-01-01 end: 2022-05-31 diff: 150
  Saturday 01.01.2022 --> Tuesday 31.05.2022: 150 days

java DatesTest 1.08.2016 07.3.2004 31.05.2022 29.2.2015 2017-11-23 7.04.2019 2000-3-6 2009-05-13

The date "29.2.2015" is illegal!
The date "2000-3-6" is illegal!
start: 2004-03-07 end: 2009-05-13 diff: 1893
  Sunday 07.03.2004 --> Wednesday 13.05.2009: 1893 days
start: 2009-05-13 end: 2016-08-01 diff: 2637
  Wednesday 13.05.2009 --> Monday 01.08.2016: 2637 days
start: 2016-08-01 end: 2017-11-23 diff: 479
  Monday 01.08.2016 --> Thursday 23.11.2017: 479 days
start: 2017-11-23 end: 2019-04-07 diff: 500
  Thursday 23.11.2017 --> Sunday 07.04.2019: 500 days
start: 2019-04-07 end: 2022-05-31 diff: 1150
  Sunday 07.04.2019 --> Tuesday 31.05.2022: 1150 days

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.