Avatar billede viciodk Praktikant
01. december 2003 - 13:13 Der er 28 kommentarer og
1 løsning

Java-kalender 2

Jeg er gang med at lave et Java-program der undersøger hvilken ugedag en bestemt dato er (uden at bruge diverse indbyggede Calender-funktioner o.lign). Men jeg får nogle fejl med denne kode:

GregorCalculations.java
-----------------------
public class GregorCalculations
{
    static final int[][] monthLengths = {{31,28,31,30,31,30,31,31,30,31,30,31},
                                          {31,29,31,30,31,30,31,31,30,31,30,31}};

    static int leapYear(int year)
    {
        int isLeapYear = 0;

        if(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            isLeapYear = 1;
        }

        return isLeapYear;
    }

    static int days_monthCounter(int month)
    {
        int days_PrevMonths = 0;

        for(int monthCounter = 0; monthCounter < month; monthCounter++)
        {
            days_PrevMonths += monthLengths[leapYear(year)][monthCounter];
        }

        return days_PrevMonths;
    }

    static int days_PrevYears(int year)
    {
        int days_PrevYears = 0;

        for(int yearCounter = 1; yearCounter < year; yearCounter++)
        {
            days_PrevYears += yearCounter * days_monthCounter(monthLengths.length);
        }

        return days_PrevYears;
    }

    static public int daysSince1_jan_1(int year, int month, int day)
    {
        return (day + days_monthCounter(month) + days_PrevYears(year));
    }
}



Gregor.java
-----------
public class Gregor
{
    static public String dayOfWeek(int year, int month, int day) //throws Exception (ikke lavet endnu)
    {
        String[] weekDays = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

        int weekDayNumber = GregorCalculations.daysSince1_jan_1(year,month,day) % 7;
        String weekDay = weekDays[weekDayNumber];

        return weekDay;
    }
}




GregorTest.java
---------------
class GregorTest
{
    public static void main(String[] args)
    {
        System.out.println(Gregor.dayOfWeek(1,1,1));
        System.out.println(Gregor.dayOfWeek(2003,11,29));
        System.out.println(Gregor.dayOfWeek(2003,11,30));
        System.out.println(Gregor.dayOfWeek(2004,4,1));
        System.out.println(Gregor.dayOfWeek(2004,2,29));
    }
}


Men jeg får denne fejl:

C:\Java>javac Gregor.java
.\GregorCalculations.java:24: cannot resolve symbol
symbol  : variable year
location: class GregorCalculations
                        days_PrevMonths += monthLengths[leapYear(year)][monthCounter];
                                                                ^
1 error

Hvad er der galt og kan det slet ikke passe at den 1. januar år 1 er en mandag?
Avatar billede erikjacobsen Ekspert
01. december 2003 - 13:16 #1
Navnet year findes ikke i days_monthCounter - måske skal den med som parameter.

Den grogorianske kalender går formelt slet ikke tilbage til år 1 - en lidt
akademisk diskussion ;)
Avatar billede arne_v Ekspert
01. december 2003 - 13:34 #2
Strengt taget går kalenderen som sådan vel tilbage til år 1. Den er
først opfundet i og brugt fra 1582. Men derofr kan man vel godt
regne baglæns. Eller kan man vel helelr ikke tale om år 122 f.kr..
Avatar billede erikjacobsen Ekspert
01. december 2003 - 13:38 #3
Men jeg synes også en akademisk diskussion er sjov ;)

Jo, man kan selvfølgelig extrapolere en hvilken som helst kalender ud over
dens grænser, og (mis)bruge værdierne. Mon der var mennesker, der på denne
extrapolerede dag, som selvfølgelig har eksisteret, har følt med sig selv
at det var år 1, at det var januar, og at det var den første i denne måned,
og at det så oven i købet var mandag?  ;)  Det tror jeg ikke ...
Avatar billede viciodk Praktikant
01. december 2003 - 13:41 #4
Hehe, vild nok diskussion ;)

Jeg prøver bare at få programmet til at virke ud fra den Gregorianske kalender og har nu rettet GregorCalculations.java til:

public class GregorCalculations
{
    static final int[][] monthLengths = {{31,28,31,30,31,30,31,31,30,31,30,31},
                                          {31,29,31,30,31,30,31,31,30,31,30,31}};

    static int leapYear(int year)
    {
        int isLeapYear = 0;

        if(((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            isLeapYear = 1;
        }

        return isLeapYear;
    }

    static int days_monthCounter(int year, int month)
    {
        int days_PrevMonths = 0;

        for(int monthCounter = 0; monthCounter < month; monthCounter++)
        {
            days_PrevMonths += monthLengths[leapYear(year)][monthCounter];
        }

        return days_PrevMonths;
    }

    static int days_PrevYears(int year)
    {
        int days_PrevYears = 0;

        for(int yearCounter = 1; yearCounter < year; yearCounter++)
        {
            days_PrevYears += days_monthCounter(year, monthLengths.length);
        }

        return days_PrevYears;
    }

    static public int daysSince1_jan_1(int year, int month, int day)
    {
        return (day + days_monthCounter(year, month) + days_PrevYears(year));
    }
}

Altså jeg får ikke nogen kompileringsfejl, men det er ikke altid man når frem til den rigtige ugedag.. Kan en af jer se hvor det går galt? :)
Avatar billede arne_v Ekspert
01. december 2003 - 13:44 #5
Har du styr på -1/0/+1 ?
Avatar billede arne_v Ekspert
01. december 2003 - 13:46 #6
Skal der være -1 fordi 1. januar år 1 skal returnere 0 og ikke 1 ?

Skal der være month-1 fordi måneds array starter i 0 og ikke 1 ?
Avatar billede erikjacobsen Ekspert
01. december 2003 - 13:54 #7
Jeg forstår slet ikke sætningen

        days_PrevYears += yearCounter * days_monthCounter(monthLengths.length);

Og så kan du optimere på mindst 2 måder.

1) Start med at finde (eller gætte) ugedagen for 1. januar 1582 - tidligere
  datoer vil alligevel ikke give mening.

2) Du behøver ikke summere dagene for alle månederne i et år - det er enten
  365 eller 366.

Og endelig kan du regne modulus 7 hele vejen igennem, hvis det kun er ugedagen
du er interesseret i.
Avatar billede viciodk Praktikant
01. december 2003 - 13:54 #8
Altså
for(int monthCounter = 0; monthCounter < month; monthCounter++)
skal laves om til
for(int monthCounter = 0; monthCounter < month-1; monthCounter++)
?
Prøver jeg lige..
Avatar billede viciodk Praktikant
01. december 2003 - 13:57 #9
erikjakobsen -> Denne sætning
        days_PrevYears += yearCounter * days_monthCounter(monthLengths.length);
.. fandt jeg også ud af er forkert. Den skal bare hedde
        days_PrevYears += days_monthCounter(year, monthLengths.length);
(det ekstra argument fordi year jo som du sagde før ikke blev fundet i days_monthCounter()
Avatar billede erikjacobsen Ekspert
01. december 2003 - 14:00 #10
Ja, ok, du ganger ikke årstallet på længere. Men det andet argument giver stadig ikke mening.
Avatar billede viciodk Praktikant
01. december 2003 - 14:08 #11
Altså jeg kalder

    static int days_monthCounter(int year, int month)
    {
        int days_PrevMonths = 0;

        for(int monthCounter = 0; monthCounter < month; monthCounter++)
        {
            days_PrevMonths += monthLengths[leapYear(year)][monthCounter];
        }

        return days_PrevMonths;
    }

Det andet argument har resultatet 12 (dvs. 12 måneder pr. år). For-løkken kører altså 12 gange og optæller antallet af dage på de 12 måneder. Men som du siger kan dette vel bare optimeres til at man skriver enten 365 eller 366 dage. Der er dog stadig brug for en monthCounter til at finde antallet af dage der er i de tidligere måneder som ligger i samme år. Til sidst adderer jeg nemlig antallet af dage med antallet af dage i årets tidligere måneder som så adderes med antallet af dage i de tidligere år (helt tilbage til år 0 (eller 1582 hvis det er det man regner med :) )).
Avatar billede erikjacobsen Ekspert
01. december 2003 - 14:10 #12
Ok, jeg var nok lidt kortfattet ;)

I eet tilfælde af kald til days_monthCounter vil du have måneden angivet
i anden parameter med i beregningen (der hvor der står 12=monthLengths.length),
og i det andet tilfælde vil du ikke, kaldet i daysSince1_jan_1. Du skal nok
tage alle angivne måneder med, og trække een fra i kaldet i daysSince1_jan_1
Avatar billede erikjacobsen Ekspert
01. december 2003 - 14:20 #13
Og iøvrigt er dette et godt eksempel på at du ikke skal gætte dig frem til om
det virker, men teste det målrettet.

Lav en main metode, og fx disse tests:

  System.out.println("Antal jan. 2003, skal være 31, giver "+days_monthCounter(2003,1));

  System.out.println("Antal feb. 2003, skal være 28, giver "+days_monthCounter(2003,2));

  System.out.println("Antal feb. 2004, skal være 29, giver "+days_monthCounter(2004,2));

...og andre ting du kender svaret på. Når de er korrekte, kan du begynde
at kigge på om det samlede resultat er korrekt.

(og check ved lejlighed www.junit.org )
Avatar billede erikjacobsen Ekspert
01. december 2003 - 14:21 #14
Hmm, tallene for februar er måske ikke som jeg skriver... ligemeget ... du
skal selvfølgelig anføre de korrekte værdier, som du jo kender.
Avatar billede trolle Nybegynder
02. december 2003 - 01:20 #15
hmm jeg lagde mærke til en lille træls fejl:
monthLengths.length    (=2)
er ikke antal måneder i et år, det er
monthLengths[0].length  (=12)

og altså skal du bruge:
days_PrevYears += days_monthCounter(year, monthLengths[0].length);
Avatar billede trolle Nybegynder
02. december 2003 - 01:24 #16
ud over det har jeg testet lidt med:

System.out.println("Antal jan. 2003, skal være 31, giver "+days_monthCounter(2003,1));
System.out.println("Antal feb. 2003, skal være 59, giver "+days_monthCounter(2003,2));
System.out.println("Antal feb. 2004, skal være 60, giver "+days_monthCounter(2004,2));
System.out.println("Antal mar. 2004, skal være 91, giver "+days_monthCounter(2004,3));
System.out.println("Antal apr. 2004, skal være 121, giver "+days_monthCounter(2004,4));
System.out.println("Antal i aar 1, giver "+days_PrevYears(2));
System.out.println("Antal i aar 2, giver "+days_PrevYears(2));

Antal jan. 2003, skal vµre 31, giver 31
Antal feb. 2003, skal vµre 59, giver 59
Antal feb. 2004, skal vµre 60, giver 60
Antal mar. 2004, skal vµre 91, giver 91
Antal apr. 2004, skal vµre 121, giver 121
Antal i aar 1, giver 365
Antal i aar 2, giver 730

hvilket lader til at vaere korrekt
Avatar billede erikjacobsen Ekspert
02. december 2003 - 01:26 #17
Godt set! - den kan man lede meeeget længe efter, hvis man kun kigger
efter om slutresultatet er korrekt eller ej. En test direkte på days_PrevYears
burde opdage den fejl noget hurtigere. (Vink: lav små tests...)
Avatar billede viciodk Praktikant
04. december 2003 - 23:12 #18
Der er nu blevet lavet en del omskrivninger i programmet, så det ser således ud:

public class Gregor
{
    static int leapYear(int year)
    {
        int isLeapYear = 0;

        if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
        {
            isLeapYear = 1;
        }

        return isLeapYear;
    }

    static int days_prevMonthsSameYear(int year, int month)
    {
        if(month != 1)
        {
            final int[][] monthLengthsRes = {{31,59,90,120,151,181,212,243,273,304,334},
                                            {31,60,91,121,152,182,213,244,274,305,335}};

            return monthLengthsRes[leapYear(year)][month - 2];
        }

        else
        {
            return 0;
        }
    }

    static public String dayOfWeek(int year, int month, int day) throws Exception
    {
        final int[][] monthLengths = {{31,28,31,30,31,30,31,31,30,31,30,31},
                                      {31,29,31,30,31,30,31,31,30,31,30,31}};

        if((year < 1 || month < 1 || month > 12 || day < 1 || day > monthLengths[leapYear(year)][month-1]))
        {
                throw new Exception("Invalid date: (" + year + "," + month + "," + day + ")");
        }

        int days_prevYearsMod = (year-1)/4 - (year-1)/100 + (year-1)/400;

        String[] weekDays = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
        int prevDaysTotalMod = day + days_prevMonthsSameYear(year, month) + days_prevYearsMod;

        return weekDays[prevDaysTotalMod % 7];
    }
}


Men den finder ikke de korrekte datoer. Der gemmer sig sikkert en ganske lille fejl et sted.. Nogen der kan se den? :)
Avatar billede viciodk Praktikant
04. december 2003 - 23:15 #19
Tror det er i int days_prevYearsMod det går galt, kan bare ikke se hvordan den skal ændres.
Avatar billede erikjacobsen Ekspert
04. december 2003 - 23:17 #20
Hvad er det du beregner i days_prevYearsMod ??
Avatar billede viciodk Praktikant
04. december 2003 - 23:42 #21
I days_prevYearsMod beregnes antallet af skudår i de tidligere år.
Avatar billede erikjacobsen Ekspert
04. december 2003 - 23:54 #22
Ja, men det tal skal vel ikke bare lægges til?

Et år, der ikke er skudår bidrager med 365 dage, og 365 = 1 (mod 7)
mens et skudår så giver 366 = 2 (mod 7)

For de år der kommer før skal du altså lægge 1 eller 2, og ikke 0 eller 1.
Du kan måske fixe det ved også at lægge (year-1) til - men lad nu være
med bare at gøre det før du ved hvad du gør.

Har du tænkt over noget med "små tests" for at se hvad der virker og hvad
der ikke virker?
Avatar billede viciodk Praktikant
04. december 2003 - 23:54 #23
Ah, det kan være det virker hvis

int prevDaysTotalMod = day - 1 + days_prevMonthsSameYear(year, month) + days_prevYearsMod + year;

Kan du se at det ikke skulle være korrekt? :) Tror det virker nu. Så kan det vist heller ikke forkortes mere?
Avatar billede arne_v Ekspert
05. december 2003 - 00:24 #24
Prøv og se følgende:

import java.util.*;

public class WeekDay {
    private static final String[] dayname = {
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday" };
    // The java way
    public static String wd1(int y, int m, int d) {
        Calendar c = new GregorianCalendar();
        c.set(Calendar.YEAR, y);
        c.set(Calendar.MONTH, m-1);
        c.set(Calendar.DAY_OF_MONTH, d);
        return dayname[c.get(Calendar.DAY_OF_WEEK) - 1];
    }
    // The elegant way
    public static String wd2(int y, int m, int d) {
        int a = (14 - m) / 12;
        int yx = y - a;
        int mx = m + 12*a - 2;
        int daynum = (d + yx + yx/4 - yx/100 + yx/400 +31*mx/12) % 7;
        return dayname[daynum];
    }
    // The straigth forward way
    private static boolean isLeapYear(int y) {
        return ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0));
    }
    private static final int[][] month = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
                                          {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335} };
    public static String wd3(int y, int m, int d) {
        int daynum = 0;
        daynum += ((y-1) * 365 + (y-1)/4 - (y-1)/100 + (y-1)/400); // years
        daynum += month[isLeapYear(y)?1:0][m-1]; // months
        daynum += (d - 1); // days
        daynum = (daynum + 1) % 7; // first day of week = monday
        return dayname[daynum];
    }
    private static void test(int y, int m, int d) {
        System.out.println(wd1(y, m, d) + " " + wd2(y, m, d) + " " + wd3(y, m, d));
    }
    public static void main(String[] args) {
        test(2003, 12, 4);
        test(2003, 12, 24);
        test(2004, 12, 8);
        test(2000, 12, 31);
        test(1900, 1, 1);
        for(int y = 1800; y <= 2100; y++) {
            for(int m = 1; m <= 12; m++ ) {
                for(int d = 1; d <= 28; d++) {
                    if(wd1(y, m, d) != wd2(y, m, d)) {
                        System.out.println("problem wd2 : " + d + "-" + m + "-" + y);
                    }
                    if(wd1(y, m, d) != wd3(y, m, d)) {
                        System.out.println("problem wd3 : " + d + "-" + m + "-" + y);
                    }
                }
            }
        }
    }
}
Avatar billede erikjacobsen Ekspert
05. december 2003 - 06:42 #25
Og den elegante måde er muligvis ikke een arne_v selv har fundet på....;)
http://www.gogole.com/search?hl=da&ie=UTF-8&oe=UTF-8&q=%2B%22zellers+congruence%22&btnG=Google-s%C3%B8gning&lr=
Avatar billede arne_v Ekspert
05. december 2003 - 07:36 #26
Jeg mener at jeg i et tidligere spørgsmål har givet viscio linket:
  http://www.tondering.dk/claus/cal/node3.html#SECTION00350000000000000000

(den site har *alt* om kalendre)
Avatar billede viciodk Praktikant
28. december 2003 - 23:26 #27
Smider I et svar? Mange tak for hjælpen :)
Avatar billede arne_v Ekspert
28. december 2003 - 23:37 #28
svar
Avatar billede viciodk Praktikant
31. december 2004 - 02:18 #29
Erik: Du har ikke lagt et svar, men hvis du også vil have nogle point, så skriv en kommentar :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester