[OK] CHEATSHEET DATETIME PYTHON - VERSION COMPLÈTE


[OK] 1. IMPORTS

from datetime import datetime, date, time, timedelta, timezone
from calendar import monthrange, day_name, month_name
import time as time_module  # Pour sleep, time() etc.

[OK] 2. OBTENIR DATE/HEURE ACTUELLE


# Date et heure complètes (avec timezone locale par défaut)
now = datetime.now()
print(now)  # 2025-11-07 14:30:45.123456

# Date seulement (sans heure)
today = date.today()
print(today)  # 2025-11-07

# Heure UTC (temps universel coordonné)
utc_now = datetime.now(timezone.utc)
print(utc_now)  # 2025-11-07 14:30:45.123456+00:00

# Timestamp Unix (secondes depuis 1970-01-01)
timestamp = datetime.now().timestamp()
print(timestamp)  # 1699365045.123456

[OK] 3. CRÉER DES DATES ET DATETIMES


# Créer une date (année, mois, jour)
d = date(2025, 11, 7)

# Créer un datetime (année, mois, jour, heure, minute, seconde)
dt = datetime(2025, 11, 7, 14, 30, 45)

# Avec microsecondes
dt_micro = datetime(2025, 11, 7, 14, 30, 45, 123456)

# Avec timezone
dt_utc = datetime(2025, 11, 7, 14, 30, 45, tzinfo=timezone.utc)

# Créer une heure seule
t = time(14, 30, 45)  # heure, minute, seconde
t_micro = time(14, 30, 45, 123456)  # avec microsecondes

# Depuis un timestamp
dt_from_ts = datetime.fromtimestamp(1699365045)
dt_from_ts_utc = datetime.fromtimestamp(1699365045, tz=timezone.utc)

# Depuis un ordinal (nombre de jours depuis 01/01/0001)
dt_ordinal = date.fromordinal(738500)

[OK] 4. ACCÉDER AUX COMPOSANTS

dt = datetime.now()

# Composants basiques
dt.year         # 2025 - année
dt.month        # 11 - mois (1-12)
dt.day          # 7 - jour du mois (1-31)
dt.hour         # 14 - heure (0-23)
dt.minute       # 30 - minute (0-59)
dt.second       # 45 - seconde (0-59)
dt.microsecond  # 123456 - microseconde (0-999999)

# Informations sur le jour
dt.weekday()     # 0-6 (lundi=0, dimanche=6)
dt.isoweekday()  # 1-7 (lundi=1, dimanche=7)

# Récupérer date ou heure séparément
dt.date()        # Retourne l'objet date
dt.time()        # Retourne l'objet time (sans timezone)
dt.timetz()      # Retourne l'objet time (avec timezone si présente)

# Numéro de semaine ISO
dt.isocalendar()  # Retourne (année_ISO, semaine, jour_semaine)
iso_year, iso_week, iso_day = dt.isocalendar()

# Ordinal (nombre de jours depuis 01/01/0001)
dt.toordinal()

# Timezone info
dt.tzinfo        # Objet timezone ou None
dt.utcoffset()   # Différence avec UTC ou None

[OK] 5. FORMATER (DATETIME -> STRING)

dt = datetime.now()

# Formats courants
dt.strftime('%Y-%m-%d')                    # '2025-11-07' - ISO date
dt.strftime('%d/%m/%Y')                    # '07/11/2025' - format français
dt.strftime('%m/%d/%Y')                    # '11/07/2025' - format US
dt.strftime('%Y-%m-%d %H:%M:%S')           # '2025-11-07 14:30:45' - datetime complet
dt.strftime('%d/%m/%Y %H:%M')              # '07/11/2025 14:30' - sans secondes

# Formats avec noms
dt.strftime('%A, %d %B %Y')                # 'Thursday, 07 November 2025'
dt.strftime('%a %d %b %Y')                 # 'Thu 07 Nov 2025'
dt.strftime('%A %d %B %Y à %H:%M')         # 'Thursday 07 November 2025 à 14:30'

# Formats 12h avec AM/PM
dt.strftime('%I:%M %p')                    # '02:30 PM'
dt.strftime('%I:%M:%S %p')                 # '02:30:45 PM'

# Formats avec timezone
dt.strftime('%Y-%m-%d %H:%M:%S %Z')        # '2025-11-07 14:30:45 UTC'
dt.strftime('%Y-%m-%d %H:%M:%S %z')        # '2025-11-07 14:30:45 +0000'

# Format complet personnalisé
dt.strftime('Le %A %d %B %Y à %H:%M:%S')   # 'Le Thursday 07 November 2025 à 14:30:45'

[OK] 6. PARSER (STRING -> DATETIME)


# Parser avec format spécifié
dt = datetime.strptime('2025-11-07', '%Y-%m-%d')
dt = datetime.strptime('07/11/2025', '%d/%m/%Y')
dt = datetime.strptime('07/11/2025 14:30', '%d/%m/%Y %H:%M')
dt = datetime.strptime('2025-11-07 14:30:45', '%Y-%m-%d %H:%M:%S')

# Parser format ISO
dt = datetime.fromisoformat('2025-11-07')
dt = datetime.fromisoformat('2025-11-07T14:30:45')
dt = datetime.fromisoformat('2025-11-07T14:30:45+00:00')

# Parser date ISO
d = date.fromisoformat('2025-11-07')

# Parser time ISO
t = time.fromisoformat('14:30:45')

[OK] 7. CODES DE FORMAT COMPLETS


# ANNÉE
# %Y    Année complète (4 chiffres)        2025
# %y    Année courte (2 chiffres)          25

# MOIS
# %m    Mois en chiffres (01-12)           11
# %B    Nom complet du mois                November
# %b    Nom abrégé du mois                 Nov

# JOUR
# %d    Jour du mois (01-31)               07
# %j    Jour de l'année (001-366)          311

# JOUR DE LA SEMAINE
# %A    Nom complet du jour                Thursday
# %a    Nom abrégé du jour                 Thu
# %w    Jour de semaine (0-6, dim=0)       4
# %u    Jour de semaine ISO (1-7, lun=1)   4

# SEMAINE
# %U    Semaine de l'année (dim=1er jour)  44
# %W    Semaine de l'année (lun=1er jour)  44

# HEURE
# %H    Heure 24h (00-23)                  14
# %I    Heure 12h (01-12)                  02
# %p    AM/PM                              PM

# MINUTE/SECONDE
# %M    Minute (00-59)                     30
# %S    Seconde (00-59)                    45
# %f    Microseconde (000000-999999)       123456

# TIMEZONE
# %z    Offset UTC (+HHMM ou -HHMM)        +0000
# %Z    Nom timezone                       UTC

# COMBINAISONS
# %c    Représentation locale complète     Thu Nov  7 14:30:45 2025
# %x    Date locale                        11/07/25
# %X    Heure locale                       14:30:45

# DIVERS
# %%    Caractère % littéral               %

[OK] 8. TIMEDELTA (DURÉES)


# Créer des durées
delta = timedelta(days=7)                          # 7 jours
delta = timedelta(hours=2, minutes=30)             # 2h30
delta = timedelta(weeks=2, days=3, hours=5)        # 2 semaines, 3 jours, 5h
delta = timedelta(seconds=3600)                    # 1 heure
delta = timedelta(milliseconds=500)                # 500 millisecondes
delta = timedelta(microseconds=1000)               # 1000 microsecondes

# Durée négative
delta = timedelta(days=-7)                         # Il y a 7 jours

# Propriétés d'un timedelta
delta = timedelta(days=2, hours=5, minutes=30)
delta.days          # 2 - nombre de jours complets
delta.seconds       # 19800 - secondes restantes (< 1 jour)
delta.microseconds  # 0 - microsecondes restantes (< 1 seconde)
delta.total_seconds()  # 190800.0 - durée totale en secondes

# Constantes utiles
timedelta.min       # timedelta(-999999999) - durée minimum
timedelta.max       # timedelta(999999999, 86399, 999999) - durée maximum
timedelta.resolution  # timedelta(0, 0, 1) - plus petite différence

[OK] 9. ARITHMÉTIQUE SUR LES DATES

now = datetime.now()
today = date.today()

# Addition avec timedelta
future_dt = now + timedelta(days=7)                # Dans 7 jours
future_dt = now + timedelta(hours=2, minutes=30)   # Dans 2h30
future_date = today + timedelta(days=30)           # Dans 30 jours

# Soustraction avec timedelta
past_dt = now - timedelta(days=30)                 # Il y a 30 jours
past_dt = now - timedelta(hours=12)                # Il y a 12 heures

# Différence entre deux dates (retourne timedelta)
dt1 = datetime(2025, 11, 1, 10, 0)
dt2 = datetime(2025, 11, 7, 15, 30)
diff = dt2 - dt1
print(diff)                 # 6 days, 5:30:00
print(diff.days)            # 6
print(diff.seconds)         # 19800 (5h30 en secondes)
print(diff.total_seconds()) # 543000.0

# Différence entre deux dates
d1 = date(2025, 11, 1)
d2 = date(2025, 11, 7)
diff = d2 - d1
print(diff.days)            # 6

# Multiplication et division de timedelta
delta = timedelta(hours=2)
double = delta * 2          # 4 heures
half = delta / 2            # 1 heure
ratio = delta / timedelta(hours=1)  # 2.0 (ratio)

# Modulo
delta = timedelta(hours=25)
remainder = delta % timedelta(hours=24)  # 1 heure

# Valeur absolue
delta = timedelta(days=-5)
abs_delta = abs(delta)      # 5 jours

[OK] 10. COMPARAISONS

dt1 = datetime(2025, 11, 1)
dt2 = datetime(2025, 11, 7)

# Comparaisons classiques
dt1 < dt2       # True - dt1 est avant dt2
dt1 > dt2       # False - dt1 est après dt2
dt1 <= dt2      # True
dt1 >= dt2      # False
dt1 == dt2      # False - dates identiques
dt1 != dt2      # True - dates différentes

# Trouver min/max
dates = [datetime(2025, 11, 1), datetime(2025, 11, 7), datetime(2025, 11, 3)]
min_date = min(dates)  # datetime(2025, 11, 1)
max_date = max(dates)  # datetime(2025, 11, 7)

# Trier des dates
dates_sorted = sorted(dates)  # Ordre croissant
dates_sorted_desc = sorted(dates, reverse=True)  # Ordre décroissant

[OK] 11. TIMESTAMPS UNIX


# Datetime -> timestamp (secondes depuis 1970-01-01 00:00:00 UTC)
dt = datetime.now()
ts = dt.timestamp()
print(ts)  # 1699365045.123456

# Timestamp -> datetime (timezone locale)
dt = datetime.fromtimestamp(1699365045)

# Timestamp -> datetime UTC
dt_utc = datetime.fromtimestamp(1699365045, tz=timezone.utc)

# Datetime UTC depuis timestamp
dt_utc = datetime.utcfromtimestamp(1699365045)

# Time module pour timestamp actuel
import time as time_module
current_ts = time_module.time()  # Timestamp actuel

[OK] 12. FORMAT ISO 8601

dt = datetime.now()

# Datetime -> ISO string
iso_str = dt.isoformat()              # '2025-11-07T14:30:45.123456'
iso_str = dt.isoformat(sep=' ')       # '2025-11-07 14:30:45.123456'
iso_str = dt.isoformat(timespec='seconds')  # '2025-11-07T14:30:45'

# Date -> ISO string
iso_date = date.today().isoformat()   # '2025-11-07'

# Time -> ISO string
iso_time = time(14, 30, 45).isoformat()  # '14:30:45'

# ISO string -> datetime
dt = datetime.fromisoformat('2025-11-07T14:30:45')
dt = datetime.fromisoformat('2025-11-07T14:30:45+00:00')
dt = datetime.fromisoformat('2025-11-07 14:30:45')

[OK] 13. TIMEZONES


# UTC timezone
utc_tz = timezone.utc
now_utc = datetime.now(utc_tz)

# Timezone avec offset fixe
paris_tz = timezone(timedelta(hours=1))        # UTC+1
ny_tz = timezone(timedelta(hours=-5))          # UTC-5
tokyo_tz = timezone(timedelta(hours=9))        # UTC+9

# Créer datetime avec timezone
dt_paris = datetime(2025, 11, 7, 14, 30, tzinfo=paris_tz)
dt_now_utc = datetime.now(timezone.utc)

# Convertir timezone (astimezone)
utc_dt = datetime.now(timezone.utc)
paris_dt = utc_dt.astimezone(paris_tz)

# Ajouter timezone à datetime naïf (replace)
naive_dt = datetime.now()
aware_dt = naive_dt.replace(tzinfo=timezone.utc)

# Retirer timezone info
aware_dt = datetime.now(timezone.utc)
naive_dt = aware_dt.replace(tzinfo=None)

# Vérifier si datetime est aware ou naive
dt = datetime.now()
is_aware = dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
is_naive = dt.tzinfo is None or dt.tzinfo.utcoffset(dt) is None

[OK] 14. CALENDRIER (MODULE CALENDAR)


# Obtenir nombre de jours dans un mois
year, month = 2025, 2
first_weekday, num_days = monthrange(year, month)
print(num_days)  # 28 (ou 29 pour année bissextile)

# Premier et dernier jour du mois
today = date.today()
first_day = date(today.year, today.month, 1)
last_day_num = monthrange(today.year, today.month)[1]
last_day = date(today.year, today.month, last_day_num)

# Noms des jours et mois (en anglais par défaut)
print(day_name[0])     # 'Monday'
print(month_name[1])   # 'January'

# Vérifier année bissextile
from calendar import isleap
is_leap = isleap(2024)  # True
is_leap = isleap(2025)  # False

[OK] 15. MÉTHODES UTILITAIRES


# Remplacer des composants (retourne nouveau datetime)
dt = datetime(2025, 11, 7, 14, 30, 45)
new_dt = dt.replace(year=2026)           # Changer année
new_dt = dt.replace(month=12, day=25)    # Changer mois et jour
new_dt = dt.replace(hour=0, minute=0, second=0, microsecond=0)  # Minuit

# Combiner date et time
d = date(2025, 11, 7)
t = time(14, 30, 45)
dt = datetime.combine(d, t)

# Séparer en date et time
dt = datetime.now()
d = dt.date()
t = dt.time()

# Format ctime (C standard)
dt = datetime.now()
ctime_str = dt.ctime()  # 'Thu Nov  7 14:30:45 2025'

# Représentation pour debugging
dt = datetime.now()
repr_str = repr(dt)     # "datetime.datetime(2025, 11, 7, 14, 30, 45, 123456)"

[OK] 16. EXEMPLES PRATIQUES


# 1. Calculer l'âge en années
birthdate = date(1990, 5, 15)
today = date.today()
age = (today - birthdate).days // 365
# Plus précis:
age = today.year - birthdate.year - ((today.month, today.day) < (birthdate.month, birthdate.day))

# 2. Début et fin du mois actuel
today = date.today()
first_day_month = date(today.year, today.month, 1)
last_day_num = monthrange(today.year, today.month)[1]
last_day_month = date(today.year, today.month, last_day_num)

# 3. Prochain lundi
today = date.today()
days_ahead = 0 - today.weekday()  # 0 = lundi
if days_ahead <= 0:
    days_ahead += 7
next_monday = today + timedelta(days=days_ahead)

# 4. Dernier jour du mois précédent
today = date.today()
first_day_this_month = date(today.year, today.month, 1)
last_day_prev_month = first_day_this_month - timedelta(days=1)

# 5. Durée d'exécution de code
start = datetime.now()
# ... votre code ici ...
time_module.sleep(2)  # Simulation
end = datetime.now()
duration = end - start
print(f"Durée: {duration.total_seconds():.2f} secondes")

# 6. Vérifier si une date est un week-end
d = date.today()
is_weekend = d.weekday() >= 5  # 5=samedi, 6=dimanche

# 7. Nombre de jours ouvrables entre deux dates
def count_weekdays(start_date, end_date):
    days = (end_date - start_date).days
    weeks = days // 7
    extra_days = days % 7
    weekdays = weeks * 5
    for i in range(extra_days):
        if (start_date + timedelta(days=i)).weekday() < 5:
            weekdays += 1
    return weekdays

# 8. Ajouter des mois (pas natif en Python)
def add_months(dt, months):
    month = dt.month - 1 + months
    year = dt.year + month // 12
    month = month % 12 + 1
    day = min(dt.day, monthrange(year, month)[1])
    return dt.replace(year=year, month=month, day=day)

# 9. Formater durée lisible
def format_duration(seconds):
    days = int(seconds // 86400)
    hours = int((seconds % 86400) // 3600)
    minutes = int((seconds % 3600) // 60)
    secs = int(seconds % 60)
    parts = []
    if days > 0:
        parts.append(f"{days}j")
    if hours > 0:
        parts.append(f"{hours}h")
    if minutes > 0:
        parts.append(f"{minutes}m")
    if secs > 0 or not parts:
        parts.append(f"{secs}s")
    return " ".join(parts)

# 10. Vérifier si deux dates sont le même jour
dt1 = datetime(2025, 11, 7, 10, 30)
dt2 = datetime(2025, 11, 7, 15, 45)
same_day = dt1.date() == dt2.date()

[OK] 17. AVEC PYTZ (TIMEZONES AVANCÉES)
[OK] Installation: pip install pytz

try:
    import pytz
    
    # Lister toutes les timezones disponibles
    all_tz = pytz.all_timezones  # Liste de ~500 timezones
    common_tz = pytz.common_timezones  # Timezones courantes
    
    # Créer timezone par nom
    paris_tz = pytz.timezone('Europe/Paris')
    ny_tz = pytz.timezone('America/New_York')
    tokyo_tz = pytz.timezone('Asia/Tokyo')
    
    # Datetime avec pytz timezone
    paris_now = datetime.now(paris_tz)
    
    # Localiser datetime naïf (préféré à replace)
    naive_dt = datetime(2025, 11, 7, 14, 30)
    paris_dt = paris_tz.localize(naive_dt)
    
    # Convertir entre timezones
    utc_dt = datetime.now(pytz.utc)
    ny_dt = utc_dt.astimezone(ny_tz)
    tokyo_dt = utc_dt.astimezone(tokyo_tz)
    
    # Normaliser après arithmétique (important pour DST)
    paris_dt = paris_tz.localize(datetime(2025, 3, 30, 2, 0))
    one_hour_later = paris_dt + timedelta(hours=1)
    normalized = paris_tz.normalize(one_hour_later)
    
except ImportError:
    print("pytz n'est pas installé. Utilisez: pip install pytz")

[OK] 18. AVEC DATEUTIL (PARSING ET MANIPULATION AVANCÉS)
[OK] Installation: pip install python-dateutil

try:
    from dateutil import parser, relativedelta
    from dateutil.rrule import rrule, DAILY, MONTHLY, YEARLY
    
    # Parser automatique (détecte le format)
    dt = parser.parse("Nov 7, 2025")
    dt = parser.parse("7/11/2025")
    dt = parser.parse("2025-11-07 14:30")
    
    # Relativedelta (plus puissant que timedelta)
    dt = datetime(2025, 11, 7)
    future = dt + relativedelta.relativedelta(months=3)  # Ajouter 3 mois
    future = dt + relativedelta.relativedelta(years=1, months=2, days=3)
    
    # Remplacer avec relativedelta
    future = dt + relativedelta.relativedelta(day=1)  # Premier jour du mois
    future = dt + relativedelta.relativedelta(month=12, day=31)  # 31 décembre
    
    # Récurrence de dates (rrule)
    # Tous les jours pendant 7 jours
    dates = list(rrule(DAILY, count=7, dtstart=datetime.now()))
    
    # Tous les premiers du mois pendant 12 mois
    dates = list(rrule(MONTHLY, count=12, bymonthday=1, dtstart=datetime.now()))
    
except ImportError:
    print("dateutil n'est pas installé. Utilisez: pip install python-dateutil")

[OK] 19. BONNES PRATIQUES ET PIÈGES


# PIÈGE 1: Datetime naïf vs aware
# Évitez de mélanger datetimes naïfs et aware
naive = datetime.now()
aware = datetime.now(timezone.utc)
# naive < aware  # TypeError!

# PIÈGE 2: Fuseaux horaires et DST
# Utilisez pytz.localize() plutôt que replace() pour les timezones
# Replace ne gère pas le DST correctement

# PIÈGE 3: Ajouter des mois
# timedelta ne supporte pas les mois (durée variable)
# Utilisez dateutil.relativedelta ou une fonction custom

# PIÈGE 4: Comparaison de dates
# date.today() != datetime.now().date() en termes de type
# Mais == fonctionne pour la comparaison de valeurs

# BONNE PRATIQUE 1: Toujours utiliser UTC en interne
# Convertir en timezone locale uniquement pour l'affichage

# BONNE PRATIQUE 2: Utiliser ISO 8601 pour le stockage
# Format standard, non ambigu, facilement parsable

# BONNE PRATIQUE 3: Validation des entrées utilisateur
try:
    user_date = datetime.strptime(user_input, '%Y-%m-%d')
except ValueError:
    print("Date invalide")

[OK] 20. PERFORMANCE ET OPTIMISATION


# Pour parser beaucoup de dates au même format, réutilisez le format
format_str = '%Y-%m-%d'
dates = [datetime.strptime(d, format_str) for d in date_strings]

# Pour comparer uniquement dates (pas heures), comparez les objets date
dt1.date() == dt2.date()  # Plus rapide que comparer datetime complets

# Cache timezone objects si utilisés fréquemment
utc = timezone.utc  # Réutiliser plutôt que recréer

[OK] FIN DU CHEATSHEET
