Generate random dates

1

I am making a program that randomly generates dates and prints them, if the date falls on a holiday, print a message such as: Happy Halloween! Now, if the chosen month is February and the day is one that February does not have, such as 29, 30 or 31, it is subtracted 3 a day. The same with the other months that only have 30 days. But I find it tedious to execute the function every time I want a date to be generated, so I want at the beginning of the program to ask me how many dates I want generated, and to print them to a list, all at random.

Here's the program:

import random
def fechas():
#La lista de meses y días posibles

meses = ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]
dias = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]

#Elije un mes y un día al azar

m = random.choice(meses) 
d = random.choice(dias)

#Si a ciertos meses se le asigna un día más alto se le resta 3 o 1 al día.

if m == "febrero" and d == 31 or d == 30 or d == 29:
    d = (d)-3
if m == "abril" and d == 31:
    d = (d)-1
if m == "junio" and d == 31:
    d = (d)-1
if m == "septiembre" and d == 31:
    d = (d)-1
if m == "noviembre" and d == 31:
    d = (d)-1

#Si la fecha coincide con una festiva, imprime un mensaje

if m == "enero" and d == 1:
    print("¡Feliz año nuevo!")
if m == "octubre" and d == 31:
    print("¡Feliz Halloween!")
if m == "diciembre" and d == 24:
    print("¡Feliz navidad!")
print(str(d) + " de " + str(m) + ".")

I want to know how to do what I said before, and that it automatically generates the number of dates I want.

    
asked by Mateo Bahntje 31.10.2017 в 00:39
source

2 answers

0

To make it a bit easier with the days of each month you could use the module calendar . Using the function% co_of% whose second parameter returns the number of days of the month (such as the days of February for leap years):

>>> import calendar
>>> calendar.monthrange(month=2, year=2017)
(2, 28)
>>> calendar.monthrange(month=2, year=2020) # Bisiesto
(5, 29)
>>> calendar.monthrange(month=12, year=2020)
(1, 31)

By the way, the first parameter is the day of the week of the first day of the month (counting from 0 as Monday). With the number of days of the month you will be able to validate the correct day as you will see later.

It seems to me that you will also get complicated if you work with the months as text instead of numbers (that you can format later):

>>> dias = range(1, 32) # No se incluye el 32
>>> meses = range(1, 13) # No se incluye el 13

For holidays you can map the dates you want as tuples of type monthrange or (mes, dia) as you prefer:

>>> festividades = {
...   (1, 1): '¡Feliz año nuevo!',
...   (31, 10): '¡Feliz Halloween!',
...   (25, 12): '¡Feliz Navidad!'
... }

Putting everything together in a script:

# -*- coding: utf-8 -*-
import calendar
from datetime import date, datetime
import random


def fechas(dia=None, mes=None):
    mensajes_festivos = {
        (1, 1): '¡Feliz año nuevo!',
        (31, 10): '¡Feliz Halloween!',
        (25, 12): '¡Feliz Navidad!'
    }
    hoy = date.today()

    # Si no hemos pasado el día o el mes en la función seleccionamos un al azar
    if not dia:
        dia = random.choice(range(1, 32))
    if not mes:
        mes = random.choice(range(1, 13))

    # Hay que usar el día válido, verifiquemos que no se pase del rango. No nos
    # interesa por ahora el primer parámetro retornado, solo la cantidad de días
    # del mes
    _, dia_maximo = calendar.monthrange(year=hoy.year, month=mes)
    if dia > dia_maximo:
        dia = dia_maximo

    # El mensaje final es una mezcla del mensaje festivo junto con el mensaje de
    # la descripción del día. Si no hay día destivo mapeado simplemente no se
    # imprime nada
    fecha = date(hoy.year, mes, dia)
    mensaje = [
        fecha.strftime('%d de %B del %Y')
    ]
    mensaje_festivo = mensajes_festivos.get((dia, mes), '')
    if mensaje_festivo:
        mensaje.append(mensaje_festivo)

    # Mostrar mensaje
    print(' '.join(mensaje))


if __name__ == '__main__':
    fechas() # Sin parámetros, dia y mes al azar
    fechas(dia=31, mes=10) # Probemos con Halloween
    fechas()
    fechas(dia=31, mes=2) # ¿31 de febrero? Hmm, no.
    fechas(dia=25, mes=12) # ¿Qué tal con Navidad?

My script is called test.py, let's execute it:

$ python3 test.py
02 de May del 2017
31 de October del 2017 ¡Feliz Halloween!
20 de February del 2017
28 de Frebruary del 2017
25 de December del 2017 ¡Feliz Navidad!

It works but ¡ugh! ... is in English. You can use the wonderful (dia, mes) to solve this problem.

$ sudo pip3 install babel
Collecting babel
  Downloading Babel-2.5.1-py2.py3-none-any.whl (6.8MB)
    100% |████████████████████████████████| 6.8MB 85kB/s 
Requirement already satisfied: pytz>=0a in /usr/local/lib/python3.5/dist-packages (from babel)
Installing collected packages: babel
Successfully installed babel-2.5.1
$ python3
Python 3.5.2 (default, Nov 17 2016, 17:05:23) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from babel.dates import format_date
>>> from datetime import date
>>> format_date(date(2017, 10, 31), locale='es')
'31 oct. 2017'
>>> format_date(date(2017, 10, 31), format='long', locale='es')
'31 de octubre de 2017'

Excellent! Now we just have to add it to the script and we have everything ready (this time without all the comments):

# -*- coding: utf-8 -*-
import calendar
from babel.dates import format_date
from datetime import date, datetime
import random


def fechas(dia=None, mes=None):
    mensajes_festivos = {
        (1, 1): '¡Feliz año nuevo!',
        (31, 10): '¡Feliz Halloween!',
        (25, 12): '¡Feliz Navidad!'
    }
    hoy = date.today()

    if not dia:
        dia = random.choice(range(1, 32))
    if not mes:
        mes = random.choice(range(1, 13))

    _, dia_maximo = calendar.monthrange(year=hoy.year, month=mes)
    if dia > dia_maximo:
        dia = dia_maximo

    fecha = date(hoy.year, mes, dia)
    mensaje = [
        format_date(fecha, format='long', locale='es_PE')
    ]
    mensaje_festivo = mensajes_festivos.get((dia, mes), '')
    if mensaje_festivo:
        mensaje.append(mensaje_festivo)

    print(' '.join(mensaje))


if __name__ == '__main__':
    fechas() # Sin parámetros, dia y mes al azar
    fechas(dia=31, mes=10) # Probemos con Halloween
    fechas()
    fechas(dia=31, mes=2) # ¿31 de febrero? Hmm, no.
    fechas(dia=25, mes=12) # ¿Qué tal con Navidad?

We see now:

$ python3 test.py 
26 de julio de 2017
31 de octubre de 2017 ¡Feliz Halloween!
9 de mayo de 2017
28 de febrero de 2017
25 de diciembre de 2017 ¡Feliz Navidad!

Everything ready.

    
answered by 31.10.2017 в 03:08
0

As for your code, you have almost everything solved, the only thing you should do is the following:

cant = int(input("Cantidad de Fechas a generar:"))
for i in range(cant):
  fechas()

That is, we ask the user for a number of dates per keyboard, using input() , we must convert it to a whole number (using int() ), because the return is a chain. Then simply a cycle for to iterate the requested times, for that we use range() which is a sequence generator and will give us the set of numbers from 0 to n-1 . Then we simply invoke your routine. If you want to save all the data in a list, you should make fecha() return the text instead of printing it on the screen, for that you should assign the values that you print to a variable and return it from the function, then you simply add it to a list , or you could also solve everything in the function ...

Some implementation issues would resolve them in another way, let's see:

  • The if to be able to adjust the number of days could be resolved if previously we have the list of dates already generated
  • Holiday dates can be handled better in a dictionary to also avoid using as many if .
  • The other thing that makes me "noise" is doing a random over days and months, because it is not a uniform distribution, there are days that appear more than others and also if you eventually ask for a whole year, it is very likely to recur days.
  • For all this, I tell you how to implement your routine:

    from random import shuffle
    import datetime
    
    def fechas(year=2017, cantidad=1):
      base = datetime.datetime(year,1,1)
      date_list = [(d.month, d.day) for d in [base + datetime.timedelta(days=x) for x in range(0, 366)] if d.year == year]
    
      meses = ["enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre"]
    
      fiestas = {(1,1):"¡Feliz año nuevo!",
                (12,25):"¡Feliz navidad!",
                (10,31):"¡Feliz Halloween!"
      }
    
      lista_fechas = []
      shuffle(date_list)
      for _ in range(cantidad):
        f = date_list.pop()
        fiesta = fiestas.get((f[0], f[1]), "")
        fiesta = fiesta + "\n" if fiesta else ""
        lista_fechas.append("{0}{1} de {2}.".format(fiesta, str(f[1]), meses[f[0]-1]))
    
      return lista_fechas
    
    cant = int(input("Cantidad de Fechas a generar:"))
    print(fechas(cantidad=cant))
    

    Some comments:

      base = datetime.datetime(year,1,1)
      date_list = [(d.month, d.day) for d in [base + datetime.timedelta(days=x) for x in range(0, 366)] if d.year == year]
    

    With the previous code we generate a list of tuples (mes, día) of a full year, of the year we say, the function receives the parameter year which by default is 2017 , when ending in date_list we go to have a list of type [(1,1), (1,2) ... (12,31)] . We also contemplate the years that are leap years. For all this we rely on ( timedelta ): base + datetime.timedelta that will return a displacement, in this case of days from the date base and taking as interval a range(0, 366) .

    The festive dates are handled as a dictionary, each key corresponds to one of the previous tuples, so that if a random value corresponds to the tuple (1,1) quickly and efficiently we can obtain the corresponding string to the festivity (1,1):"¡Feliz año nuevo!" .

    The other issue than your implementation is that instead of randomizing days and months, we use a routine from the module random : shuffle(date_list) to randomly clutter the entire date list. So then, we simply iterate up to the number of requested dates and retrieve a value and remove it from the list by f = date_list.pop() with this we make sure that if we ask 365 or 366 values we will never get a repeated value. Then we simply add this value to a list along with the eventual holiday message:

    fiesta = fiestas.get((f[0], f[1]), "")
    fiesta = fiesta + "\n" if fiesta else ""
    lista_fechas.append("{0}{1} de {2}.".format(fiesta, str(f[1]), meses[f[0]-1]))
    

    At the end we will have a list lista_fechas that will contain the dates in the format stated in your question and eventually the message of greeting of the festival. (For now all concatenated to respect your question but you can choose any other way to show this data).

        
    answered by 31.10.2017 в 20:23