Avatar billede meelby Nybegynder
14. januar 2001 - 12:38 Der er 7 kommentarer og
1 løsning

UgeNr og År - find mandag

Er der en der kan knække dette problem. Jeg har ugenummeret og året, og ud fra det skal jeg bruge datoen for mandagen i den aktuelle uge.

Jeg har søgt på mange af de indlæg der er her i eksperten, men jeg har ikke kunne få noget til at virke stabilt. Problemet er at uge for 1/1/YY - kan være 52, 53 eller 1.

Jeg har prøvet med følgende kode som desværre kun virker i nogle år ?

If Request(\"YY\")=\"\" then: YY=year(now): else: YY=Request(\"YY\") end if
If Request(\"UgeNr\")=\"\" then: UgeNr=DatePart(\"ww\", \"1/1/\" & YY, vbFirstJan1): else UgeNr = Request(\"UgeNr\"): end if
Dag1=DateSerial(YY, 1, 1)
Ugedag = Weekday(Dag1)

\'Find mandag i uge 1
if Ugedag > 2 then
  Mandag= DateAdd(\"d\", -(Ugedag-2), Dag1)
  \'Dato = Mandag
elseif Ugedag < 2 then
  Mandag= DateAdd(\"d\", 1, Dag1)
  \'Dato = DateAdd(\"ww\", UgeNr, Mandag)
else
  Mandag= DateAdd(\"d\", 0, Dag1)
end if
UgeNr1=DatePart(\"ww\", Mandag)
IF (UgeNr1 = 52 or  UgeNr1 = 53) then: Mandag = DateAdd(\"d\", 7, Mandag) end if
MandagAktuelleUge = DateAdd(\"ww\", UgeNr-1, Mandag)
Avatar billede hoejrup Nybegynder
14. januar 2001 - 13:19 #1
Jeg har engang lavet en af verdens mest avanceret planlægning kalender systemer til en stor koncern, og systemet kunne også nemt håndtere dit problem. Samt noget der er 100 gange mere komplekst med hensyn til kalendere. Nemlig flydende beregning af helligdage, der ikke falder på den samme dag hver eneste år.

Lidt hjælp: Prøv at studere kalendere for en periode på 10år, så skal du nok finde svaret.

Eks.
Hvis mandag er den 28. december, så er der 53 uger i året. Ellers der kun 52 uger i året.

/per
Avatar billede steffen Nybegynder
14. januar 2001 - 13:33 #2
(Det kan man da vist kalde hjælp til selvhjælp, eh? :-))
Avatar billede hoejrup Nybegynder
14. januar 2001 - 14:09 #3
steffen> Jeg gider ikke sidde og skrive 150 programlinier for 500 point. I øvrigt har jeg aldrig programmeret i VBscript før. Men jeg vil gerne give ham nogle hints.
Avatar billede steffen Nybegynder
14. januar 2001 - 14:16 #4
Han var nok mere interesseret i formlen (den er vel ens for alle sprog), end \"Prøv at studere kalendere ..\" - det var mest det jeg lige synes der lød en smule sjovt ..

Men det var ikke fordi jeg synes din hjælp var malplaceret.
Avatar billede tknudsen Nybegynder
18. januar 2001 - 15:33 #5
Jeg har nogle funktioner som bla giver dig svar på dit spørgsmål. De er lavet til delphi - og bruger nogle specifikke typer som feks TTimeStamp.

Jeg sender dig den unit de ligger i.

Funktionen du er intereseret i er:
DateOfWeek : integer;year : word) : TDateTime;
- som returnerer datoen på mandagen i ugen.


Avatar billede tknudsen Nybegynder
18. januar 2001 - 15:35 #6
eeh - men jeg kan ikke lige finde din email ! findes den her på eksperten.dk ?
Avatar billede meelby Nybegynder
28. januar 2001 - 12:00 #7
Lukker spørgsmålet.
Avatar billede steffen Nybegynder
29. januar 2001 - 23:00 #8
Fandt en lille sjov fil, der måske kan bruges i sammenhængen:

--

Beregning af ugedag og dage mellem to datoer.
=============================================

Først lidt historie
===================

For at kunne lave beregninger på datoer, må man først finde ud
af hvordan kalenderen fungerer. Det \"tropiske år\" er ikke på 365 døgn,
men på 365,24219878 døgn. Da den overskydende del siden oldtiden
har været erkendt til at være et kvart døgn, indførte Julius Cæsar,
efter forslag af grækeren Sosigenes, i år 46. f. Kr. den
\"julianske kalender\". Ifølge den julianske kalender skulle år
delelig med fire være skudår, dvs. indeholde 366 dage i stedet
for normalt 365 dage. Det julianske år har derfor længden 365,25 dage.

Årslængden på 365,25 dage gjorde dog at der i 1500 tallet var kommet
en afvigelse på ca. 12-13 døgn, så i 1582 indførte Pave Gregor d. 13.
den \"gregorianske kalender\". Samtidig med inførelsen af denne kalender
blev datoen rettet med ca 14 dage.

Den gregorianske kalender har, ligesom den julianske, skudår hvert
fjerde år, men til gengæld er år delelige med 100 ikke skudår,
undtagen år der er delelig med 400, som er skudår.
Dette giver en årslængde på i snit 365,32425 dage, meget tæt
på det ønskede. Denne kalender giver kun en fejl på ca. 3 dage
på 10000 år. Så er man perfektionist, så burde år delelig med 4000
ikke være skudår.

Den gregorianske kalender blev desvære indført af paven lige efter
Luther og reformationen, så mange protestantiske fyrstedømmer og lande
indførte først den nye kalender meget senere.
I Danmark blev den gregorianske kalender indført i marts 1700.
(Samtidig med at der skete et spring i datoen på ca. 14 dage).
Den russisk ortodokse kirke benytter stadig den gamle julianske kalender,
og det er derfor de fejrer jul omtrent på samme tidspunkt som vi
fejrer Hellig Tre Konger.

Skal man derfor til at beregne ugedag, eller antal dage mellem
to datoer, vil en algoritme kun give fornuftige data, såfremt
man tager hensyn til hvilket land det drejer sig om, og datoen
for den gregorianske kalenders indførelse, samt hvilken dato
man foretog justeringen.

Opbygning af algoritmen
=======================

Det første man opdager ved at se på kalenderen er at den er opbygget
af år, måneder, og dage, hvor der ikke umiddelbart ser ud til
at være nogen pæn linær sammenhæng. Et \"brute-force\" kodet program til
ugedags beregning vil derfor kunne komme til at bestå af mange \"if/case\"
kontruktioner til at tage højde for de forskellige regler.

For at undgå dette indser man at der må laves en anden skala, f.eks.
antal dage siden et \"fiktiv\" år 0. Dette er meget nemmere at udregne,
idet reglerne i den gregorianske kalender er meget klare.
Så antallet af dage fra år 0 til den 0. januar kan så beregnes
på følgende måde:

    /* der benyttes heltals divison ! */
    Faktor = 365*ÅR + (ÅR-1)/4 - (ÅR-1)/100 + (ÅR-1)/400 ;

Forkortes lidt fås:

    Faktor = 365*ÅR + (ÅR-1)/4 - ((ÅR-1)/100)*3/4 ;

Næste problem bliver februar, som kan være på enten 28 eller 29 dage.
Alle andre måneder har et fast antal dage. Det nemmeste er
at flytte årets start til marts, idet februar så kommer sidst,
så behøves der ikke at tages hensyn til om det er skudår eller
ej, når den aktuelle \"faktor\" skal udregnes for en given dato.
Nu mangler der så kun, at få lavet en linær sammenhæng mellem
dag i året og aktuel måned og dag. Hvis man starter med at tælle
fra marts får man følgende tabel:

        antal  dag i
    Måned    dage    året-
      ------------------------
    Marts      31      0
    April      30    31
    Maj      31    61
    Juni      30    92
    Juli      31    122
    August      31    153
    September  30  184
    Oktober      31    214
    November  30    245
    December  31  275
    Januar      31  306
      ( Februar  28/29  337  )
      ( I alt        365/366 )

Nu kunne en sådan tabel løsning være ideel, og de fleste ville nok kunne
stille sig tilfreds her. Algoritmen ville komme til at se ud som
følgende:

  /* der benyttes heltals divison ! */
  if (Måned < 3 ) {
    /* da skuddagen er den 29/2, skal skuddag ikke medregnes i jan/feb */
    /* så derfor benyttes ÅR-1  */
    Faktor = 365*ÅR + (ÅR-1)/4 - (((ÅR-1)/100)*3)/4
          + tabel[Måned] + Dag.
  } else  {
    Faktor = 365*ÅR + ÅR/4 - ((ÅR/100)*3)/4
          + tabel[Måned] + Dag.
  }

Tabellen kan forenkles noget. Plotter man tabellen ind på millimeterpapir,
og gives Januar og Februar hhv. månedsnummeret 13 og 14, vil man opdage,
at man (næsten) kan tegne en ret linje i gennem de plottede punkter.
Ved enten af aflæse, eller ved at benytte linær regression,
kan man få hældningskoefficient (m) og skæring med Y-aksen (b):

    m = 30,6013986 ,  b = -91,77855478 .

Ved beregningen fremkom korrelationskoefficienten 0,999996, som er
ganske godt, da 1,00 ville være fuldkommen korrelation.
Tabellen fra før kan derfor erstattes med:

    round ( Måned*30,60 - 91,78 )
    = Int ( Måned*30,60 - 91,78 + 0,5 )
    = Int ( Måned*30,60 - 91,28 )

For ikke at skulle benytte reelle tal (float), udregnes det hele
i en heltal udgave, og samtidig forenkles beregningen ved Januar
og Februar. Den færdige rutine bliver så:

  /* ------------------- factor -----------------------------*/
  #define LONGWORD unsigned int

  LONGWORD factor(RTCtime *tm)
  {
    WORD  y,m;

    y = tm->year;
    m = tm->month;
    if ( tm->month < 3 ) {  y--; m += 12; }
    return 365UL*y + y/4UL - ((y/100+1)*3)/4 + (m*3060-9135)/100
      + tm->day + 59;
  }

Datastrukturen for RTCtime er lavet for at holde lidt styr på
dag, måned og år.  Den kan ses erklæret øverst i  testdate.c .


Beregning af ugedag:
====================

Der er 7 dage i ugene, og dagene gentager sig hele tiden, så
beregning af ugedag kan nemt gøres ved modulus 7. Ønskes derudover
at søndag er ugedag nr. 0, skal der først adderes 6 til faktoren.

    /*------------------- weekday ------------------------------*/
    /* returns weekday, sun=0, mon=1,..... : */
    int weekday(RTCtime *tm)
    { 
        return (int) ( (factor(tm)+6) % 7UL );
    }

For at se hvordan man kan udskrive ugedagen, se på
proceduren  write_date()  i  testdate.c .

Beregning af antal dage mellem to datoer
========================================

Denne kan gøres meget enkel med  \"factor()\":

    Dage = labs ( factor(Dato1) - factor(Dato2) ).

C-funktionen  \"labs()\"  udregner den absolute værdi.


Kontrol af om en dato er ok
===========================

Ved indtastning af datoer er der ofte behov for at kontrollere
om datoen er gyldig, så f.eks. datoer som d. 29 februar 1995 eller
31 april ikke accepteres. Året kontrolleres på sædvanlig vis, evt.
suppleret med, at det for Danmark skal være fra og med marts 1700.
Måneden er nem at kontrollere, idet det selfølgelig er et tal
fra 1 til 12. Antallet af dage i måneden kan med  factor()  let
beregnes, idet den fremgår som antallet af dage mellem den første
i den aktuelle måned og den 1. i næste måned. En simpel procedure
til at foretage en sådan kontrol er  \"date_ok()\".

    /*------------------- date_ok ------------------------------*/
    int date_ok(RTCtime *t)
    /* returns 0 if date not ok, else days in month is returned */
    {
      LONGWORD f1;
      RTCtime tmp;

      if ( (t->year  < 1) || (t->year  >9998) ) return 0;
      if ( (t->month < 1) || (t->month >  12) ) return 0;
      if (  t->day  < 1 ) return 0;
      tmp = *t; tmp.day=1;
      f1 = factor(&tmp);
      tmp.month++;
      if ( (f1 = factor(&tmp)-f1) < t->day ) return 0;
      else  return (int) f1; /* if ok return days in month */
    }


Adder N dage til en dato, og udregn den nye dato.
=================================================

Det nemmeste er at udregne  factor()  for datoen  og addere de N dage: 

              F = factor(dato) + N ;

Derefter divideres med 365.2425, og det fremkomne tal skulle gerne være
årstallet:
            ÅR= F * 10000UL / 3652425UL;

Som kontrol udregnes  F1 = factor( 1.jan.ÅR), og  F2 = factor( 1.jan.(ÅR+1).
Hvis der på grund af afrundingsfejl ikke gælder uligheden:

          F1 <= F < F2

Må  ÅR korrigeres med +/- 1.

Måneden findes lettes ved at udregne  Fm = factor(1.MD.ÅR) for alle
måneder startende med  MD=1,  indtil  Fm > F. Dagen kan så findes
som diffeencen mellem Fm og F.

Denne sidste del er ikke vist i programmet  testdate.c , men kan
være en lille opgave til næste nummer af  MCUG.




Alle ovenstående programstumper kan ses i testdata.c, som er medtaget
på medlemsdisketten. Programmet kan også downloades fra BBS\'et.
Ovenstående procedurer kan også findes i  \"rtcprocs.c\"  fra
filen RTCLK092.ZIP, der tidligere har været omtalt i bladet.


                    Frank Damgaard

Litteraturhenvisning:
  TI Programmerbar 58/59, Standardbibliotek, 1977, (manual).
  Nordisk Konversations Leksikon.
  Focus, 1972, Gjellerup.
  Databog i fysik og kemi. F&K forlaget. ISBN 87-87229-09-9 .
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