Sonntag ab ein bestimmtes Datum zählen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Filtik
User
Beiträge: 3
Registriert: Mittwoch 16. November 2011, 19:25

Hallo zusammen,

ich möchte für ein Spiel ein Adventskranz machen und möchte da Python nicht jedes Jahr neu umschreiben sonderns einmal richtig machen und fertig.

Ein freund von mir hat mir diese notlösung gegeben:

Code: Alles auswählen

    def xMasKranz(self):
        global AgeStartedIn
        if (AgeStartedIn == PtGetAgeName()):
            dnitime = PtGetDniTime()
            dayNum = int(time.strftime('%d', time.gmtime(dnitime)))
            monthNum = int(time.strftime('%m', time.gmtime(dnitime)))
            #sparkNum = int(sdlCalStar.value[-2:])
            #sdlName = sdlVars[sparkNum]
            sdlName = xMasKranzsdl
            sdl = PtGetAgeSDL()
            sdl.setFlags(sdlName, 1, 1)
            sdl.sendToClients(sdlName)
            #THESE DATES ARE FOR 2011 ONLY!
            #THESE DATES ARE FOR 2011 ONLY!
            #THESE DATES ARE FOR 2011 ONLY!
            #THESE DATES ARE FOR 2011 ONLY!
            #THESE DATES ARE FOR 2011 ONLY!
            if (monthNum == 12):
                if (dayNum >= 18):
                    sdl.setIndex(sdlName, 0, 4)
                    PtDebugPrint('codxMas: Current state is 4 - enabling')
                else if (dayNum >= 11):
                    sdl.setIndex(sdlName, 0, 3)
                    PtDebugPrint('codxMas: Current state is 3 - enabling')
                else if (dayNum >= 4):
                    sdl.setIndex(sdlName, 0, 2)
                    PtDebugPrint('codxMas: Current state is 2 - enabling')
                else:
                    sdl.setIndex(sdlName, 0, 1)
                    PtDebugPrint('codxMas: Current state is 1 - enabling')
            else if (monthNum == 11):
                if (dayNum >= 27):
                    sdl.setIndex(sdlName, 0, 1)
                    PtDebugPrint('codxMas: Current state is 1 - enabling')
                else:
                    sdl.setIndex(sdlName, 0, 0)
                    PtDebugPrint('codxMas: Current state is 0 - disabling')
            else:
                sdl.setIndex(sdlName, 0, 0)
                PtDebugPrint('codxMas: Current state is 0 - disabling')
Ich möchte es eigentlich so haben, das er vom Datum 26.11. (im jeden Jahr) ab die Sonntage zählt und eine Variable auf diese Zahl ändert.
z.B.: 27.11.2011 ist der erste Sonntag nach dem 26.11.2011 und gibt mir eine 1, am 04.12.2011 eine 2 usw.
in der Zeit zwischen 27.11.2011 - 03.12.2011 soll die Variable auf der 1 gesetzt bleiben.

ich hoffe, es kann mir jemand helfen da es nicht mehr lange hin ist.
wenn ich es unklar mein Problem dargestellt hab, versuch ich es besser zu beschreiben.

gruß
Filtik
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Filtik hat geschrieben:Ich möchte es eigentlich so haben, das er vom Datum 26.11. (im jeden Jahr) ab die Sonntage zählt und eine Variable auf diese Zahl ändert.
Die Erklärung ist etwas unklar und der Code enthält diverse Bestandteile die mit dem Problem nichts zu tun haben.

Ich würde das ohnehin von der anderen Seite beginnen. Du brauchst die 4 Sonntage vor dem 1. Weihnachtstag. Dann ermitteln wir doch einfach den ersten Weihnachtstag, schauen dann wie viele Tage wir bis zum vorhergehenden Sonntag rückwärts gehen müssen und gehen dann insgesamt 4 Wochen zurück. Das datetime-Modul hat alles was man dafür braucht.

Code: Alles auswählen

import datetime

def get_sunday_in_advent(year):
    christmas = datetime.date(year, 12, 25)
    offset = datetime.timedelta(days=christmas.isoweekday())
    sundays = [christmas - offset - datetime.timedelta(weeks=week) for week in xrange(4)]
    sundays.sort()
    return sundays

dates = get_sunday_in_advent(2010)
print(dates)
Jetzt hast du eine Liste der Adventssonntage und kannst damit machen was du möchtest.

Edit: Nach mehrmaligem Lesen habe ich nun auch verstanden welche Werte du als Ergebnis haben möchtest. Ab dem 1. Adventssonntag bis inklusive dem Samstag vor dem 2. Adventssonntag die 1, dann in den folgenden 7 Tagen die 2 und so weiter. Das lässt sich leicht erreichen. Ergänzen wir den obigen Code doch einfach um folgendes Beispiel:

Code: Alles auswählen

from bisect import bisect
print(bisect(dates, datetime.date(2010, 12, 1)))
Es wirkt ein wenig magisch wenn man bisect nicht kennt. In dem Fall sollte man der Dokumentation zu dem Modul mal einen oder zwei Blicke gönnen.
Filtik
User
Beiträge: 3
Registriert: Mittwoch 16. November 2011, 19:25

Okay, ich hoff das ich es richtig verstanden hab und auch richtig umgesetzt hab.

zu vergleich, hier mein PythonFile, diesesmal complett:

Code: Alles auswählen

from Plasma import *
from PlasmaTypes import *
from PlasmaKITypes import *
from bisect import bisect
import time
import datetime


# ---------
# globals
# ---------

AgeStartedIn = None
xMasSuprisesdl = 'Event12'
xMasKranzsdl = 'Event14'


class codxMas(ptResponder):

    def __init__(self):
        ptResponder.__init__(self)
        self.id = 8501007
        self.version = 1


    def OnFirstUpdate(self):
        global AgeStartedIn
        AgeStartedIn = PtGetAgeName()
        if (AgeStartedIn in ['cityofdimensions']):
            self.xMasSuprise()
            self.xMasKranz()


    def OnServerInitComplete(self):
        self.xMasSuprise()
        self.xMasKranz()


    def get_sunday_in_advent(self, year):
        christmas = datetime.date(year, 12, 25)
        offset = datetime.timedelta(days=christmas.isoweekday())
        sundays = [christmas - offset - datetime.timedelta(weeks=week) for week in xrange(4)]
        sundays.sort()
        return sundays


    def xMasSuprise(self):
        global AgeStartedIn
        if (AgeStartedIn == PtGetAgeName()):
            dnitime = PtGetDniTime()
            dayNum = int(time.strftime('%d', time.gmtime(dnitime)))
            monthNum = int(time.strftime('%m', time.gmtime(dnitime)))
            sdlName = xMasSuprisesdl
            sdl = PtGetAgeSDL()
            sdl.setFlags(sdlName, 1, 1)
            sdl.sendToClients(sdlName)
            if (monthNum == 12):
                if (dayNum <= 24):
                    sdl.setIndex(sdlName, 0, dayNum)
                    PtDebugPrint(('codxMas: Current month is %d, day is %d - enabling' % (monthNum, dayNum)))
                else:
                    sdl.setIndex(sdlName, 0, 24)
                    PtDebugPrint(('codxMas: Current month is %d, day is %d - enabling' % (monthNum, dayNum)))
            else:
                sdl.setIndex(sdlName, 0, 0)
                PtDebugPrint(('codxMas: Current month is %d, day is %d - disabling' % (monthNum, dayNum)))


    def xMasKranz(self):
        global AgeStartedIn
        if (AgeStartedIn == PtGetAgeName()):
            sdlName = xMasKranzsdl
            sdl = PtGetAgeSDL()
            sdl.setFlags(sdlName, 1, 1)
            sdl.sendToClients(sdlName)
            dates = self.get_sunday_in_advent(2010)
            sundays = bisect(dates, datetime.date(2010, 12, 1))
            if (sundays <= 4):
                sdl.setIndex(sdlName, 0, sundays)
                PtDebugPrint(('codxMas: Current Adventweek is %d - enabling' % (sundays)))
            else:
                sdl.setIndex(sdlName, 0, 0)
                PtDebugPrint(('codxMas: Current Adventweek is %d - disabling' % (sundays)))
                
hoffentlich kann man es jetzt besser verstehen.

die DEF get_sunday_in_advent hab ich übernommen und wird von der DEF xMasKranz abgefragt. *hoff ich*
Bin eigentlich ein totaler Python nuub und bin mal froh wenn was geht, was ich geschrieben hab.

Also währe ich für jede korrektur dankbar.
Benutzeravatar
/me
User
Beiträge: 3554
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Filtik hat geschrieben:

Code: Alles auswählen

    def xMasKranz(self):
        global AgeStartedIn
        if (AgeStartedIn == PtGetAgeName()):
            sdlName = xMasKranzsdl
            sdl = PtGetAgeSDL()
            sdl.setFlags(sdlName, 1, 1)
            sdl.sendToClients(sdlName)
            dates = self.get_sunday_in_advent(2010)
            sundays = bisect(dates, datetime.date(2010, 12, 1))
            if (sundays <= 4):
                sdl.setIndex(sdlName, 0, sundays)
                PtDebugPrint(('codxMas: Current Adventweek is %d - enabling' % (sundays)))
            else:
                sdl.setIndex(sdlName, 0, 0)
                PtDebugPrint(('codxMas: Current Adventweek is %d - disabling' % (sundays)))
Also währe ich für jede korrektur dankbar.
global ist böse. Versuche darauf zu verzichten.

if-statements benötigen keine Klammern um die Bedingung.

Der von dir gewählte Bezeichnername sundays ist irreführend. Der daran gebundene Wert entspricht der Adventswoche und daher wäre week oder week_in_advent ein besserer Name. Das mag jetzt wie eine unnütze Kleinigkeit erscheinen. Wenn man allerdings bedenkt, dass Programmcode deutlich häufiger gelesen als geschrieben wird und Bezeichnernamen zum Verständnis des Programms nicht unwesentlich beitragen, dann ist jede unglückliche Namensvergabe eine zu viel.

In deinem vorherigen Code hattest du das Datum dynamisch ermittelt. Das solltest du vielleicht wieder machen statt als Jahr generell 2010 anzunehmen und immer nur auf den 1. Dezember zu vergleichen.
Filtik
User
Beiträge: 3
Registriert: Mittwoch 16. November 2011, 19:25

okay, hab mir das zu herzen genommen und nach langen hin und her, siehts jetzt so aus:

Code: Alles auswählen

    def get_sunday_in_advent(self):
        isyear = int(time.strftime('%Y', time.gmtime(dnitime)))
        christmas = datetime.date(isyear, 12, 25)
        offset = datetime.timedelta(days=christmas.isoweekday())
        sundays = [christmas - offset - datetime.timedelta(weeks=week) for week in xrange(4)]
        sundays.sort()
        return sundays


    def xMasKranz(self):
        sdlName = xMasKranzsdl
        sdl = PtGetAgeSDL()
        sdl.setFlags(sdlName, 1, 1)
        sdl.sendToClients(sdlName)
        dayNum = int(time.strftime('%d', time.gmtime(dnitime)))
        monthNum = int(time.strftime('%m', time.gmtime(dnitime)))
        yearNum = int(time.strftime('%Y', time.gmtime(dnitime)))
        dates = self.get_sunday_in_advent()
        isadventweek = bisect(dates, datetime.date(yearNum, monthNum, dayNum))
        sdl.setIndex(sdlName, 0, isadventweek)
        PtDebugPrint(('codxMas: Current Adventweek is %d' % (isadventweek)))
Bei der ersten DEF wird nun automatisch das Jahr genommen, was gerade ist.
Bei der zweiten DEF in "bisect" sollte ich ja immer das aktuelle Datum haben, das währe damit auch erledigt.

Ich wusste nicht genau was "bisect" macht, auch nachdem ich es nachgelesen hab. aber nach langem hin und her doch noch verstanden.

kleine frage nebenbei noch, im welchen PythonFile ist dieses "datetime" drin?
BlackJack

@Filtik: Die Namensgebung ist teilweise etwas ungewöhnlich. Was soll denn das `is` bei `isyear` und `isadventweek` bedeuten? Die Vorsilbe `is` deutet eigentlich auf eine Funktion die etwas testet und einen Wahrheitswert zurück gibt, oder in selteneren Fällen eben ein Name für einen Wahrheitswert. Man sollte ausserdem auf Abkürzungen verzichten wenn diese nicht geläufig sind. Unter `dni` in `dnitime` kann ich mir zum Beispiel nichts vorstellen.

Die Kombination aus `int()`, `time.strftime()`, und `time.gmtime()` ist total kompliziert und unnötig — schau Dir doch mal an was `gmtime()` als Rückgabewert hat:

Code: Alles auswählen

In [16]: time.gmtime()
Out[16]: time.struct_time(tm_year=2011, tm_mon=11, tm_mday=18, tm_hour=3, tm_min=33, tm_sec=57, tm_wday=4, tm_yday=322, tm_isdst=0)

In [17]: time.gmtime().tm_year
Out[17]: 2011

In [18]: time.gmtime().tm_mon
Out[18]: 11

In [19]: time.gmtime().tm_mday
Out[19]: 18

In [20]: time.gmtime()[:3]
Out[20]: (2011, 11, 18)
Wenn Du das `datetime`-Modul verwendest kannst Du aber auch ganz auf `time.gmtime()` verzichten.

Desweiteren sind beide Methoden eigentlich keine Methoden sondern Funktionen. Man sollte sie deshalb auch entweder als Funktionen implementieren, oder zumindest mittels `staticmethod()` deutlich machen, dass das Absicht ist.

Es kommen Namen von „irgendwo her“ statt als Argumente übergeben zu werden. Zumindest im Fall von `dnitime` scheint es sich dabei nicht um einen konstanten Wert zu handeln. Das ist unsauber.

Die Funktionen könnten letztendlich so aussehen (ungetestet):

Code: Alles auswählen

from bisect import bisect
from datetime import date as Date, datetime as DateTime, timedelta as TimeDelta


def get_sundays_in_advent(dnitime):
    christmas = Date.fromtimestamp(dnitime).replace(month=12, day=25)
    offset = TimeDelta(days=christmas.isoweekday())
    return [
        christmas - offset - TimeDelta(weeks=week)
        for week in reversed(xrange(4))
    ]


def xMasKranz(xMasKranzsdl, dnitime):
    sdl = PtGetAgeSDL()
    sdl.setFlags(xMasKranzsdl, 1, 1)
    sdl.sendToClients(xMasKranzsdl)
    sundays = get_sundays_in_advent(dnitime)
    isadventweek = bisect(sundays, Date.fromtimestamp(dnitime))
    sdl.setIndex(xMasKranzsdl, 0, isadventweek)
    PtDebugPrint('codxMas: Current Adventweek is %d' % (isadventweek))
Der Datentyp `datetime` ist im Modul `datetime` zu finden. In welcher Datei der Code für Module steckt, verrät normalerweise das `__file__`-Attribut auf dem Modul:

Code: Alles auswählen

In [6]: import datetime

In [7]: datetime.__file__
Out[7]: '/usr/lib/python2.6/lib-dynload/datetime.so'
In diesem Fall ist das also kein Modul was in Python geschrieben ist.
Antworten