Sunday, June 4, 2006

Datum en tijd (Dutch)

Introductie

Java kent twee hoofdklassen waarmee je met de datum en tijd kunt spelen: Date en Calendar. Let op, de Date bevat flink wat deprecated (afgeschreven) methoden, zoals getDate(), getHours(), getMonth(), etcetera. Zie ook het linkje. Er wordt verwacht om niet meer van deze methoden gebruik te maken. Het is de bedoeling om bij datumbewerkingen zoveel mogelijk van Calendar gebruik te maken. De Date mag nog wel worden gebruikt om de datums te tonen en te vergelijken.

De standaard aanroep en uitvoer:

package test;

import java.util.Calendar;
import java.util.Date;

public class Test {
    
    public static void main(String[] args) {

        // Maak nieuwe Date en Calendar instanties.
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();

        // Laat de inhoud zien.
        System.out.println(date);
        System.out.println(calendar);
    }

}

Sat Jun 03 22:07:28 CEST 2006
java.util.GregorianCalendar[time=1149365248906, areFieldsSet=true, areAllFieldsSet=true, lenient=true, zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin", offset=3600000, dstSavings=3600000, useDaylight=true, transitions=143, lastRule=java.util.SimpleTimeZone[id=Europe/Berlin, offset=3600000, dstSavings=3600000, useDaylight=true, startYear=0, startMode=2, startMonth=2, startDay=-1, startDayOfWeek=1, startTime=3600000, startTimeMode=2, endMode=2, endMonth=9, endDay=-1, endDayOfWeek=1, endTime=3600000, endTimeMode=2]], firstDayOfWeek=2, minimalDaysInFirstWeek=4, ERA=1, YEAR=2006, MONTH=5, WEEK_OF_YEAR=22, WEEK_OF_MONTH=1, DAY_OF_MONTH=3, DAY_OF_YEAR=154, DAY_OF_WEEK=7, DAY_OF_WEEK_IN_MONTH=1, AM_PM=1, HOUR=10, HOUR_OF_DAY=22, MINUTE=7, SECOND=28, MILLISECOND=906, ZONE_OFFSET=3600000, DST_OFFSET=3600000]

Met de standaard aanroep wordt de huidige datum en tijd automatisch ingesteld. Verder kun je ook zien dat de Calendar meer uitgebreidere informatie bevat.

Terug naar boven

Datum en tijd instellen

Aangezien de instelmethoden van de Date klasse afgeschreven zijn, wordt hier alleen die van de Calendar behandeld. Je kunt daarmee op verschillende manieren de datum en tijd instellen:

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Maak een paar nieuwe Calendar instanties.
        Calendar c1 = Calendar.getInstance();
        Calendar c2 = Calendar.getInstance();
        Calendar c3 = Calendar.getInstance();
        Calendar c4 = Calendar.getInstance();

        // Maak deze leeg. Niet noodzakelijk, maar wel netjes.
        c1.clear();
        c2.clear();
        c3.clear();
        c4.clear();

        // Stel op verschillende manieren de datum en tijd in.

        // Stel de datum en tijd stuk voor stuk in.
        c1.set(Calendar.YEAR, 2006);
        c1.set(Calendar.MONTH, 5); // Let op: de maandtelling begint met 0!
        c1.set(Calendar.DATE, 3);
        c1.set(Calendar.HOUR, 22);
        c1.set(Calendar.MINUTE, 7);
        c1.set(Calendar.SECOND, 28);

        // Stel de datum en tijd in een keer in.
        c2.set(2006, 5, 3, 22, 7, 28);

        // Stel de datum en tijd zonder seconden in.
        c3.set(2006, 5, 3, 22, 7);

        // Stel de datum in.
        c4.set(2006, 5, 3);

        // Laat de inhoud zien in de vorm van een Date.
        System.out.println(c1.getTime());
        System.out.println(c2.getTime());
        System.out.println(c3.getTime());
        System.out.println(c4.getTime());
    }

}

Sat Jun 03 22:07:28 CEST 2006
Sat Jun 03 22:07:28 CEST 2006
Sat Jun 03 22:07:00 CEST 2006
Sat Jun 03 00:00:00 CEST 2006

Terug naar boven

Converteren tussen Date en Calendar

Je kunt dus een Calendar ook converteren naar Date en omgekeerd:

package test;

import java.util.Calendar;
import java.util.Date;

public class Converters {

    // Converteer Date naar Calendar
    public static Calendar dateToCalendar(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    // Converteer Calendar naar Date
    public static Date calendarToDate(Calendar calendar) {
        return calendar.getTime();
    }

}
Terug naar boven

Converteren tussen Calendar en String

Hiervoor bestaat een handige klasse: de SimpleDateFormat subklasse van DateFormat. Het werkt als volgt:

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class Converters {

    // Converteer Calendar naar String
    public static String calendarToString(Calendar calendar, String dateFormat){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        return simpleDateFormat.format(calendar.getTime());
    }

    // Converteer String naar Calendar
    public static Calendar stringToCalendar(String dateString, String dateFormat){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        Calendar calendar = null;

        try {
            simpleDateFormat.parse(dateString);
            calendar = simpleDateFormat.getCalendar();
        } catch (ParseException e) {
            // De dateString en/of de dateFormat zijn ongeldig. Doe jouw ding om dit
            // probleem af te handelen: doe niks en retourneer null of werp een Exceptie.
        }

        return calendar;
    }

}

De voorbeelden voor de dateFormat parameter staan reeds in de SimpleDateFormat klasse definitie beschreven, kijk onder "Date and Time Patterns" en "Examples". Hieronder heb je enkele gebruiksvoorbeelden.

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Maak een nieuwe Calendar instantie.
        Calendar calendar = Calendar.getInstance();

        // Converteer deze naar verschillende Strings.
        System.out.println(Converters.calendarToString(calendar, "dd-MM-yyyy HH:mm:ss"));
        System.out.println(Converters.calendarToString(calendar, "E d MMM y"));

        // Converteer verschillende strings naar een Calendar.
        System.out.println(Converters.stringToCalendar("03-06-2006", "dd-MM-yyyy"));
        System.out.println(Converters.stringToCalendar("test", "dd-MM-yyyy"));
    }

}

03-06-2006 22:07:28
za 3 jun 06
java.util.GregorianCalendar[time=1149285600000, areFieldsSet=true, areAllFieldsSet=false, lenient=true, zone=sun.util.calendar.ZoneInfo[id="Europe/Berlin", offset=3600000, dstSavings=3600000, useDaylight=true, transitions=143, lastRule=java.util.SimpleTimeZone[id=Europe/Berlin, offset=3600000, dstSavings=3600000, useDaylight=true, startYear=0, startMode=2, startMonth=2, startDay=-1, startDayOfWeek=1, startTime=3600000, startTimeMode=2, endMode=2, endMonth=9, endDay=-1, endDayOfWeek=1, endTime=3600000, endTimeMode=2]], firstDayOfWeek=2, minimalDaysInFirstWeek=4, ERA=?, YEAR=2006, MONTH=5, WEEK_OF_YEAR=?, WEEK_OF_MONTH=?, DAY_OF_MONTH=3, DAY_OF_YEAR=?, DAY_OF_WEEK=?, DAY_OF_WEEK_IN_MONTH=?, AM_PM=?, HOUR=?, HOUR_OF_DAY=?, MINUTE=?, SECOND=?, MILLISECOND=?, ZONE_OFFSET=?, DST_OFFSET=?]
null

Wat betreft de laatste resultaat, wanneer het converteren van String naar Calendar is mislukt, dan retourneert de converter dus een leeg Calendar object: null. Het is handig om het verkregen object op null te controleren, voordat je verder ermee gaat werken.

Terug naar boven

Converteren tussen Date en String

Hiervoor kun je uiteraard ook de SimpleDateFormat gebruiken. Je kunt de hierboven getoonde Calendar-String converters na een kleine aanpassing gewoon herbruiken:

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Converters {

    // Converteer Date naar String
    public static String dateToString(Date date, String dateFormat){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        return simpleDateFormat.format(date);
    }

    // Converteer String naar Date
    public static Date stringToDate(String dateString, String dateFormat){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
        Date date = null;

        try {
            date = simpleDateFormat.parse(dateString);
        } catch (ParseException e) {
            // De dateString en/of de dateFormat zijn ongeldig. Doe jouw ding om dit
            // probleem af te handelen: doe niks en retourneer null of werp een Exceptie.
        }

        return date;
    }

}
Terug naar boven

Verschil tussen datums berekenen

De standaard Sun Java API biedt hier geen specifieke functionaliteit voor. Je zou in principe het verschil tussen de tijd in seconden kunnen berekenen, echter dit houdt totaal geen rekening met de schrikkeldagen, schrikkeljaren en schrikkeleeuwen(!) en is derhalve alleen nuttig om de verstreken uren, minuten en seconden te berekenen. Maar je kunt met de methoden Calendar#before() en Calendar#after() testen of de ene calendar van een eerdere danwel een latere datum dan de andere calendar is. Wanneer je dit in een while loop doet met een incrementele counter op het gewenste veld, dan kun je het verschil in het gewenste veld berekenen. Dit is handig voor het berekenen van het verschil in jaren, maanden en dagen. Hieronder volgt een basisvoorbeeldje om het verschil in dagen te berekenen:

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Maak een nieuwe Calendar instantie en vul deze met de geboortedatum van BalusC.
        Calendar geboortedatum = Calendar.getInstance();
        geboortedatum.clear();
        geboortedatum.set(1978, 2, 26); // 26 maart 1978.

        // Maak een nieuwe Calendar instantie met de huidige datum (3 juni 2006 22:07:28).
        Calendar vandaag = Calendar.getInstance();

        // Voorbereid de teller. We beginnen met -1, omdat een gelijke datum ook wordt meegeteld.
        int elapsedDays = -1;

        // Zolang vandaag later is dan de geboortedatum ..
        while (vandaag.after(geboortedatum)) {

            // .. voeg elke keer 1 dag toe aan de geboortedatum ..
            geboortedatum.add(Calendar.DATE, 1);

            // .. en houd bij hoeveel dagen er verstreken zijn.
            elapsedDays++;
        }

        // Laat deze zien.
        System.out.println("BalusC is dus " + elapsedDays + " dagen oud.");
    }

}

BalusC is dus 10296 dagen oud.

Oei.

Enfin, dit stukje code kun je makkelijk uitbouwen zodat je de verstreken jaren, maanden en dagen afzonderlijk kunt berekenen. Zie hieronder voor de complete set:

package test;

import java.util.Calendar;

public class CalendarUtil {

    // Bereken het verschil in jaren tussen de twee Calendars.
    public static int elapsedYears(Calendar before, Calendar after){
        return elapsed(before, after, Calendar.YEAR);
    }

    // Bereken het verschil in maanden tussen de twee Calendars.
    public static int elapsedMonths(Calendar before, Calendar after){
        return elapsed(before, after, Calendar.MONTH);
    }

    // Bereken het verschil in dagen tussen de twee Calendars.
    public static int elapsedDays(Calendar before, Calendar after){
        return elapsed(before, after, Calendar.DATE);
    }

    // De centrale hulpmethode. Dit retourneert -1 indien de eerste
    // Calendar niet van een eerdere datum is dan de tweede Calendar.
    private static int elapsed(Calendar before, Calendar after, final int CALENDAR_TYPE){
        // We maken even een kopie, want deze wordt namelijk gewoon overgeschreven.
        Calendar clone = (Calendar) before.clone();
        int elapsed = -1;
        while (after.after(clone)) {
            clone.add(CALENDAR_TYPE, 1);
            elapsed++;
        }
        return elapsed;
    }

}

Het berekenen van het verschil in uren, minuten en seconden spreekt haast voor zich, haal het verschil in milliseconden eruit en reken deze terug. Dit is veel sneller dan het berekenen van deze delen in een while loop. Het casten van double naar int rondt de waarden trouwens automatisch naar beneden af.

package test;

import java.util.Calendar;

public class CalendarUtil {

    // Bereken het verschil in uren tussen de twee Calendars.
    public static int elapsedHours(Calendar before, Calendar after) {
        return (int) elapsedMillis(before, after, 360000);
    }

    // Bereken het verschil in minuten tussen de twee Calendars.
    public static int elapsedMinutes(Calendar before, Calendar after) {
        return (int) elapsedMillis(before, after, 60000);
    }

    // Bereken het verschil in seconden tussen de twee Calendars.
    public static int elapsedSeconds(Calendar before, Calendar after) {
        return (int) elapsedMillis(before, after, 1000);
    }

    // Bereken het verschil in milliseconden tussen de twee Calendars.
    public static long elapsedMillis(Calendar before, Calendar after) {
        return elapsedMillis(before, after, 1);
    }

    // De centrale hulpmethode om milliseconden terug te rekenen. Dit retourneert -1
    // indien de eerste Calendar niet van een eerdere datum is dan de tweede Calendar.
    private static long elapsedMillis(Calendar before, Calendar after, final int FACTOR) {
        if (before.after(after)) {
            return -1;
        }
        return (after.getTimeInMillis() - before.getTimeInMillis()) / FACTOR;
    }

}

Even testen:

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Maak een nieuwe Calendar instantie en vul deze met de geboortedatum van BalusC.
        Calendar geboortedatum = Calendar.getInstance();
        geboortedatum.clear();
        geboortedatum.set(1978, 2, 26); // 26 maart 1978.

        // Maak een nieuwe Calendar instantie met de huidige datum (3 juni 2006 22:07:28).
        Calendar vandaag = Calendar.getInstance();

        // Vraag de verstreken tijd in verschillende manieren op.
        System.out.println("De leeftijd van BalusC op verschillende manieren bepaald:");
        System.out.println(CalendarUtil.elapsedYears(geboortedatum, vandaag) + " jaren");
        System.out.println(CalendarUtil.elapsedMonths(geboortedatum, vandaag) + " maanden");
        System.out.println(CalendarUtil.elapsedDays(geboortedatum, vandaag) + " dagen");
        System.out.println(CalendarUtil.elapsedHours(geboortedatum, vandaag) + " uren");
        System.out.println(CalendarUtil.elapsedMinutes(geboortedatum, vandaag) + " minuten");
        System.out.println(CalendarUtil.elapsedSeconds(geboortedatum, vandaag) + " seconden");
    }

}

De leeftijd van BalusC op verschillende manieren bepaald:
28 jaren
338 maanden
10296 dagen
247125 uren
14827507 minuten
889650448 seconden

Met deze wetenschap (hoe de tijdsdelen te berekenen, niet hoe oud BalusC is) kunnen we nu als volgt het totaal verstreken tijd tot in de seconden berekenen:

package test;

import java.util.Calendar;

public class CalendarUtil {

    // Bereken het verschil in tijdsdelen tussen de twee Calendars. Dit retourneert een
    // int array met achtereenvolgens jaren, maanden, dagen, uren, minuten en seconden.
    public static int[] elapsedTime(Calendar before, Calendar after){

        // Even controleren of de invoer in orde is. Retourneer anders een rits -1'tjes.
        if (before.after(after)) {
            return new int[] {-1, -1, -1, -1, -1, -1};
        }

        // We maken even een kopie, want deze wordt namelijk gewoon overgeschreven.
        Calendar clone = (Calendar) before.clone();

        // En nu even een int array voorbereiden voor de tijdsdelen.
        int[] elapsedTime = new int[6];
        
        // Vraag de verstreken jaren op en voeg deze toe aan de kloon.
        elapsedTime[0] = elapsedYears(clone, after);
        clone.add(Calendar.YEAR, elapsedTime[0]);
        
        // Vraag de verstreken maanden op en voeg deze toe aan de kloon.
        elapsedTime[1] = elapsedMonths(clone, after);
        clone.add(Calendar.MONTH, elapsedTime[1]);

        // Vraag de verstreken dagen op en voeg deze toe aan de kloon.
        elapsedTime[2] = elapsedDays(clone, after);
        clone.add(Calendar.DATE, elapsedTime[2]);

        // Vraag de verstreken uren op en voeg deze toe aan de kloon.
        elapsedTime[3] = elapsedHours(clone, after);
        clone.add(Calendar.HOUR, elapsedTime[3]);

        // Vraag de verstreken minuten op en voeg deze toe aan de kloon.
        elapsedTime[4] = elapsedMinutes(clone, after);
        clone.add(Calendar.MINUTE, elapsedTime[4]);

        // Vraag de verstreken seconden op.
        elapsedTime[5] = elapsedSeconds(clone, after);

        return elapsedTime;
    }

}

Uiteraard testen we dit even:

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Maak een nieuwe Calendar instantie en vul deze met de geboortedatum van BalusC.
        Calendar geboortedatum = Calendar.getInstance();
        geboortedatum.clear();
        geboortedatum.set(1978, 2, 26); // 26 maart 1978.

        // Maak een nieuwe Calendar instantie met de huidige datum (3 juni 2006 22:07:28).
        Calendar vandaag = Calendar.getInstance();

        // Vraag de verstreken tijdsdelen op.
        int[] elapsedTime = CalendarUtil.elapsedTime(geboortedatum, vandaag);

        // Voorbereid de string representaties van de tijdsdelen.
        String[] tijden = new String[] {
            "jaren", "maanden", "dagen", "uren", "minuten", "seconden"
        };

        // Laat ze zien.
        System.out.print("BalusC is ");
        for (int i = 0; i < elapsedTime.length; i++) {
            System.out.print(elapsedTime[i] + " " + tijden[i] + " ");
        }
        System.out.println("oud");
    }

}

BalusC is 28 jaren 2 maanden 8 dagen 22 uren 7 minuten 28 seconden oud

Terug naar boven

Unix Timestamp in Java

Je hebt vast weleens van de Unix timestamp (Unix Epoch, Unix Time) gehoord. Dit kun je op de volgende manieren ook met Java opvragen. Let wel, je krijgt de timestamp in milliseconden terug in plaats van in seconden zoals bij onder andere PHP.

package test;

import java.util.Calendar;
import java.util.Date;

public class Test {
    
    public static void main(String[] args) {

        // Verkrijg direct de Unix timestamp.
        System.out.println(new Date().getTime());
        System.out.println(Calendar.getInstance().getTimeInMillis());
        System.out.println(System.currentTimeMillis());
    }

}

1149365248906
1149365248906
1149365248906

Terug naar boven

Werken met Unix Timestamps

Stop de timestamp gewoon in een Calendar object en dan kun je daarmee van alles doen.

package test;

import java.util.Calendar;

public class Test {
    
    public static void main(String[] args) {

        // Verkrijg op een of andere manier de Unix timestamp.
        long timestamp = System.currentTimeMillis();

        // Stel een Calendar in met de timestamp.
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(timestamp);

        // Je kunt hierna van alles met de calendar doen.
    }

}
Terug naar boven

Copyright - Er is geen copyright op de code. Je kunt het naar believen overnemen, aanpassen danwel verspreiden.

(C) Juni 2006, BalusC

2 comments:

Unknown said...

Handige post. Ik zou echter 1 ding anders doen in het voorbeeld "Datum en tijd instellen".

In plaats van
c1.set(Calendar.MONTH, 5);

Zou ik het volgende schrijven:
c1.set(Calendar.MONTH, Calendar.JUNE);

Dit voorkomt fouten met de maand-telling EN het is voor de lezer direct duidelijk om welke maand het gaat.

BalusC said...

Dit vraagt meer werk wanneer je bijvoorbeeld de maand verkrijgt vanuit een gebruikers invoer. Je zou anders ook zelf een utility klasse eromheen bouwen, zoals ik later in deze blog ook heb gedaan: CalendarUtil.