Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Java: Päivämäärien eron laskeminen

Sivun loppuun

JoPeRa [27.10.2014 21:18:36]

#

Opiskelen omaksi huvikseni Javaa, ja eteeni tuli tehtävä joka on seuraavanlainen: "Kirjoita ohjelma, joka ilmoittaa, kuinka monta vuotta vanha olet, kun annat ohjelmalle syntymäaikasi ja tämän päivän päivämäärän."

Tehtävä kuuluu aiheeseen "Valintarakenteet", enkä oikein pääse käsitykseen että miten pystyn if- tai switch-rakenteella tällaisen koodaamaan...

Ja miten käytännössä hoituu tällaisten päivämäärien (esim. 27.10.2014) vertaileminen toisiinsa?

Grez [27.10.2014 22:47:43]

#

No jos ajatellaan että saat nuo päivät muuttujiin

//syntymäpäivä: sp, sk, sv
//tänään: tp, tk, tv

Niin sittenhän se menisi jotakuinkin

ikä = tv-sv;
if (sk<tk || (sk==tk && sp<tp)) { ikä--; }

Sinänsä javamaisempaa olisi varmaan käyttää Date olioita, mutta luulisin että jos valintarakenteita halutaan niin tuollainen liippaisi lähempää.

JoPeRa [28.10.2014 19:57:29]

#

Kiitoksia vinkistä, tuo auttoi ajattelemaan asiaa joten päädyin sitten alla olevaan ratkaisuun.

import java.util.Scanner;

public class Ikä {

    static Scanner lukija = new Scanner (System.in);

    public static void main(String args[]) {
        int sp, sk, sv;
        int tp, tk, tv;
        int ikä;

        System.out.print("Anna syntymäpäiväsi: ");
        sp = lukija.nextInt();
        System.out.print("Anna syntymäkuukautesi: ");
        sk = lukija.nextInt();
        System.out.print("Anna syntymävuotesi: ");
        sv = lukija.nextInt();

        System.out.print("\nAnna tämä päivä: ");
        tp = lukija.nextInt();
        System.out.print("Anna tämä kuukausi: ");
        tk = lukija.nextInt();
        System.out.print("Anna tämä vuosi: ");
        tv = lukija.nextInt();

        ikä = tv - sv;

        if (tk < sk || tp < sp) {
            ikä--;
            System.out.println("\nIkäsi on " + ikä);
        }
        else {
            System.out.println("Ikäsi on " + ikä);
        }
    }
}

Grez [28.10.2014 20:13:48]

#

Miksi sinulla on kaksi erillistä "Ikäsi on x" tulostusta ja toisen alussa on rivinvaihto?

JoPeRa [28.10.2014 20:37:29]

#

Tuo rivinvaihto on vain omaksi ilokseni, teksti tulostuu kivasti näytölle kun siihen jää pieni väli...

Tuo toinen "Ikäsi on x" on sitä varten että ikä tulostuu myös siinä tapauksessa jos "tämä päivä" on sama tai suurempi kuin "syntymäpäivä".

jalski [28.10.2014 20:47:51]

#

JoPeRa kirjoitti:

Tuo toinen "Ikäsi on x" on sitä varten että ikä tulostuu myös siinä tapauksessa jos "tämä päivä" on sama tai suurempi kuin "syntymäpäivä".

Yksinkertaisempi olisi vain käyttää suoraan esimerkkiä, minkä Grez antoi. Eli siis if-lohkosta tulostus pois, turha else-lohko pois myös ja tulostus pelkästään yhteen paikkaan if-lohkon jälkeen.

JoPeRa [28.10.2014 21:09:43]

#

Ok. Tuo vain näytti omiin silmiin jotenkin yksinkertaisemman näköiseltä joten siksi tuollainen...

Grez [28.10.2014 21:12:48]

#

Tuntuu kyllä oudolta ajatella että

if (tk < sk || tp < sp) {
    ikä--;
    System.out.println("\nIkäsi on " + ikä);
}
else {
    System.out.println("Ikäsi on " + ikä);
}

voisi näyttää yksinkertaisemmalta kuin

if (tk < sk || tp < sp) {
    ikä--;
}
System.out.println("\nIkäsi on " + ikä);

JoPeRa [28.10.2014 21:31:07]

#

Käsitin vähän väärin... :D Eli siis nyt vasta hoksasin että tuon koko tulostuksen saa laitettua if-lauseen jälkeen. Ja tosiaan tuolla kun sanoin että näyttää yksinkertaisemmalta niin tarkoitin että tuossa näytti äkkiseltään monimutkaiselta tuo if-lause:

if (sk<tk || (sk=tk && sp<tp)) { ikä--; }

(Olen opetellut javaa vasta n. pari viikkoa joten johtunee siitäkin..)

Grez [28.10.2014 21:46:55]

#

Joo, voi olla että lause näytti monimutkaiselta, mutta tuo sinun versiosi toimii väärin. Tai mietipä jos olet syntynyt 30.1. ja tänään on 28.10. Tuo versiosi tulkitsee että sinulla ei tänä vuonna ole vielä ollut syntymäpäivää, koska 28 on pienempi kuin 30.

Tosin omassanikin oli virhe.. Vahingossa olin kirjoittanut = kun piti olla ==

JoPeRa [29.10.2014 19:16:21]

#

Grez kirjoitti:

Joo, voi olla että lause näytti monimutkaiselta, mutta tuo sinun versiosi toimii väärin. Tai mietipä jos olet syntynyt 30.1. ja tänään on 28.10. Tuo versiosi tulkitsee että sinulla ei tänä vuonna ole vielä ollut syntymäpäivää, koska 28 on pienempi kuin 30.

Niinpäs onkin... Korjataan siis koodi.

Metabolix [29.10.2014 22:11:31]

#

Laskun voisi tässä tapauksessa tehdä myös näin:

// Haetaan nykyinen päivä. Koodissa pitäisi oikeastaan vielä varmistaa aikavyöhyke ja kalenterin tyyppi.
Calendar cal = Calendar.getInstance();
int tv = cal.get(Calendar.YEAR), tk = cal.get(Calendar.MONTH)+1, tp = cal.get(Calendar.DAY_OF_MONTH);
// Valitaan syntymäpäivä.
int sv = 1900, sk = 9, sp = 3;
// Lasketaan näistä pitkät luvut ja niiden avulla erotus.
int t = 10000 * tv + 100 * tk + tp; // 20141029
int s = 10000 * sv + 100 * sk + sp; // 19000903
int ero = t - s;                    //  1140126
int ikä = ero / 10000;              //  114
System.out.println("Kekkonen olisi nyt " + ikä + " vuotta vanha.");

_Pete_ [30.10.2014 11:19:22]

#

Suosittu kirjasto aikamäärien pyörittelyyn on tämä:

http://www.joda.org/

Kun ajat on saatu jodan DateTimen niin sen apisa löytyy toimivat tavat saada kahden aikamäärän väliset kestot esim. näin:

http://www.joda.org/joda-time/key_duration.html

jalski [30.10.2014 22:57:13]

#

Kuinkas helpolla Java selviää standardikirjastonsa kanssa tehtävästä, jos ohjelman pitäisi tulostaa ikä vuosina, kuukausina ja päivinä.

Eli ohjelman tulostus olisi siis muotoa: "Olet x vuotta, x kuukautta ja x päivää vanha."

Kirjoittelin ohjelman piruuttani nopeasti PL/I:llä:

age: procedure options(main);

  dcl 1 date_type based,
        2 dd   pic '99',
        2 mm   pic '99',
        2 yyyy pic '9999';

  dcl format char(8) nonasgn init('DDMMYYYY');

  dcl (i, xx) fixed bin(31);
  dcl (d1, d2) like date_type;
  dcl (firstdays, numdays, yeardays) fixed bin(31);
  dcl (today, birthday) char(8) date('DDMMYYYY');
  dcl (temp, days_to_today) bit(*) controlled;
  dcl (y, m, d) fixed bin(31);
  dcl input char(20) var;

  ask: display('Input birthday (DDMMYYYY)') reply(input);
  if ^validdate(trim(input), format) then
    do;
       display('Not a valid birthday!');
       goto ask;
    end;

  birthday = trim(input);
  today = datetime(format);

  firstdays = days('0101' || trim(substr(today, 5, 4)), format);

  yeardays = days('0101' || trim(substr(today, 5, 4) + 1), format) -
             days('0101' || trim(substr(today, 5, 4)), format);

  numdays = days(today, format) - firstdays;

  allocate temp bit (yeardays);
  allocate days_to_today bit(numdays);

  d1.dd   = substr(birthday, 1, 2);
  d1.yyyy = substr(today, 5, 4);

  do i = 1 to 12;
    d1.mm = i;
    if validdate(string(d1), format) then
      do;
        xx = days(string(d1), format) - firstdays;
        if xx = 0 then
          xx = 1;
        substr(temp, xx, 1) = '1'b;
      end;
    else
      do;
        d2 = d1;
        d2.dd = '01';
        d2.mm += 1;
        xx = (days(string(d2), format) - 1) - firstdays;
        substr(temp, xx, 1) = '1'b;
      end;
  end;

  days_to_today = substr(temp, 1, numdays);

  free temp;

  y = substr(today, 5, 4) - substr(birthday, 5, 4);
  m = tally(days_to_today, '1'b) - substr(birthday, 3, 2);
  if m < 0 then
    do;
      m += 12;
      y -= 1;
    end;
  d = numdays - searchr(days_to_today,'1'b);

  display('You are ' || trim(y) || ' years, '
          || trim(m) || ' months and ' || trim(d) || ' days old.');

  free days_to_today;

end age;

Metabolix [31.10.2014 16:12:33]

#

jalski, en jaksa lukea epäselvää ja tarpeettoman pitkää koodiasi, mutta tehtävä on helppo ihan ilman mitään kirjastoa. Ei tarvita kuin kaksi riviä lisää edelliseen esimerkkiini, jossa laskettiin vuodet erotuksen avulla.

int t = Integer.parseInt(new SimpleDateFormat("yyyyMMdd").format(new Date()));
System.out.println("Päivämäärä nyt:    " + t);
System.out.print("Anna syntymäpäivä (YYYYMMDD): ");
int s = lukija.nextInt();
int v = (t - s) / 10000;
int k = (t - s - 10000 * v) / 100;
int p = (t - s - 10000 * v - 100 * k);
System.out.format("Ikä on %d vuotta, %d kuukautta, %d päivää.\n", v, k, p);

Sinänsä on kyseenalaista, kannattaako erotusta ylipäänsä esittää tuollaisessa muodossa. Kuukausien pituudet nimittäin tuottavat outoja tuloksia: esimerkiksi 61 vuorokauden ero voi ajankohdasta riippuen olla 1 kk 30 pv (1.7.–31.8.), 2 kk (1.3.–1.5.), 2 kk 1 pv (29.2.–30.4. karkausvuonna) tai 2 kk 2 pv (28.2.–30.4.).

Grez [31.10.2014 16:37:06]

#

Metabolix kirjoitti:

int t = Integer.parseInt(new SimpleDateFormat("yyyyMMdd").format(new Date()));
System.out.println("Päivämäärä nyt:    " + t);
System.out.print("Anna syntymäpäivä (YYYYMMDD): ");
int s = lukija.nextInt();
int v = (t - s) / 10000;
int k = (t - s - 10000 * v) / 100;
int p = (t - s - 10000 * v - 100 * k);
System.out.format("Ikä on %d vuotta, %d kuukautta, %d päivää.\n", v, k, p);

Miksi minun on jotenkin vaikea uskoa että tuo antaisi haluttuja tuloksia.

Kokeillaan

Syntymäpäivä: 19770920
Päivämäärä: 20140505

Tuloste olisi nähdäkseni:
Ikä on 36 vuotta, 95 kuukautta, 85 päivää.

Tuo ei uskoakseni ole haettu lopputulos.

jalski [31.10.2014 19:07:21]

#

Metabolix kirjoitti:

jalski, en jaksa lukea epäselvää ja tarpeettoman pitkää koodiasi, mutta tehtävä on helppo ihan ilman mitään kirjastoa. Ei tarvita kuin kaksi riviä lisää edelliseen esimerkkiini, jossa laskettiin vuodet erotuksen avulla.

Kuten Grez jo sanoi, epäilen myös vahvasti esimerkkisi antavan ei toivotunlaisen vastauksen.

Kiitos muuten Grez, antamasi esimerkki antoi ohjelmallani negatiiviset kuukaudet! ;) Alkuperäinen viestini päivitetty ja korjattu.

Oma PL/I-ohjelma toteutukseni on itse asiassa hyvinkin yksinkertainen:

Vuodet saadaan laskettua yksinkertaisesti vähennyslaskulla.

Kuukausien ja päivien laskeminen tapahtuu käyttämällä apuna bittimerkkijonoja. Ensin lasketaan tämän vuoden ensimmäisen päivän päivien määrä Lilian formaatissa. Sitten selvitetään tarvittavat puskurien koot bittimerkkijonoille, eli päivien määrä vuodessa ja päivien määrä tähän päivään asti vuoden alusta. Nyt voidaan silmukassa asettaa bittimerkkijonoon syntymäpäivää vastaava päivä jokaisen kuukauden kohdalta ykköseksi, jos kyseessä on esimerkiksi karkauspäivä, mutta ei karkausvuosi niin asetetaan kyseisen kuukauden viimeinen päivä ykköseksi.

Bittimerkkijonosta voidaan nyt ottaa tarkasteltavaksi pätkä tämän vuoden alusta tähän päivään asti. Laskemalla ykkösbittien lukumäärä ja vähentämällä siitä syntymäkuukausi, niin saadaan kuukausien määrä. Päivien määrä saadaan suorittamalla merkkijonohaku ja etsimällä oikeanpuoleisin ykkösbitti. Nyt vähentämällä päivien kokonaismäärästä haun tuloksena saatu bittimerkkijonon indeksi saadaan päivien lukumäärä.

Metabolix [31.10.2014 20:45:51]

#

Totta, korjataanpa koodi. Ei ole silti juuri sen vaikeampaa, lähinnä karkausvuoden huomiointi tuottaa lisäkoodia.

int t = Integer.parseInt(new SimpleDateFormat("yyyyMMdd").format(new Date()));
System.out.println("Päivämäärä nyt:    " + t);
System.out.print("Anna syntymäpäivä (YYYYMMDD): ");
int s = lukija.nextInt();
// Lasketaan edellisen kuukauden päivien määrä.
int tv = t / 10000, ek = (t / 100 % 100 + 10) % 12 + 1;
boolean karkaus = tv % 400 == 0 || (tv % 4 == 0 && tv % 100 != 0);
int paivat = 30 + ((ek ^ (ek >> 3)) & 1) - (ek == 2 ? karkaus ? 1 : 2 : 0);
// Erotus, kuukauden pyörähdyksen korjaus, päivän pyörähdyksen korjaus.
int v = (t - s) / 10000;
int k = ((t - s) - ((t - s) % 10000 >= 1200 ? 10000 - 1200 : 0)) / 100 % 100;
int p = ((t - s) - ((t - s) % 100 >= paivat ? 100 - paivat : 0)) % 100;
System.out.format("Ikä on %d vuotta, %d kuukautta, %d päivää.\n", v, k, p);

Grez [01.11.2014 12:08:34]

#

Minusta on jotenkin kummallista että kummatkin tuntuvat tekevän yksinkertaisen asian jotenkin turhan monimutkaisesti kikkaillen.

Metabolixin "kikka" oli tuossa vuoden laskemisessa vielä ihan siedettävä, mutta mielestäni tuossa viimeisessä esimerkissä kertomiset, jakamiset ja jakojäännökset vain monimutkaistavat hommaa tarpeettomasti.

Metabolix [01.11.2014 15:05:57]

#

Grezin mieliksi vielä yksi versio:

Date d = new Date();
int tv = Integer.parseInt(new SimpleDateFormat("yyyy").format(d));
int tk = Integer.parseInt(new SimpleDateFormat("MM").format(d));
int tp = Integer.parseInt(new SimpleDateFormat("dd").format(d));
System.out.format("Päivämäärä nyt:    %04d %02d %02d\n", tv, tk, tp);
System.out.print("Anna syntymäpäivä (YYYY MM DD): ");
int sv = lukija.nextInt();
int sk = lukija.nextInt();
int sp = lukija.nextInt();

// Lasketaan edellisen kuukauden päivien määrä.
int ek = tk > 1 ? tk - 1 : 12;
boolean karkaus = tv % 400 == 0 || (tv % 4 == 0 && tv % 100 != 0);
int paivat = 30 + ((ek ^ (ek >> 3)) & 1) - (ek == 2 ? karkaus ? 1 : 2 : 0);

// Lasketaan erotus.
int v = tv - sv;
int k = tk - sk;
int p = tp - sp;
if (p < 0) {
	p += paivat;
	k -= 1;
}
if (k < 0) {
	k += 12;
	v -= 1;
}
System.out.format("Ikä on %d vuotta, %d kuukautta, %d päivää.\n", v, k, p);

Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta