# Fichier: python_cheats/cheatsheets/time.txt
# Cheatsheet time Python - Guide Complet



[OK] IMPORT & BASICS


import time
from time import sleep, time, perf_counter

# Timestamp actuel (secondes depuis epoch)
timestamp = time.time()  # 1699564800.123456

# Dormir/pause
time.sleep(1)        # 1 seconde
time.sleep(0.5)      # 0.5 seconde
time.sleep(0.001)    # 1 milliseconde

# Epoch Unix (1er janvier 1970 00:00:00 UTC)
epoch = 0


[OK] MESURER LE TEMPS


# time() - Temps système (précision moyenne)
start = time.time()
# ... code ...
elapsed = time.time() - start
print(f"Durée: {elapsed:.4f}s")

# perf_counter() - Haute précision (RECOMMANDÉ)
start = time.perf_counter()
# ... code ...
elapsed = time.perf_counter() - start
print(f"Durée: {elapsed:.6f}s")

# perf_counter_ns() - Nanosecondes
start = time.perf_counter_ns()
# ... code ...
elapsed_ns = time.perf_counter_ns() - start
print(f"Durée: {elapsed_ns} ns")

# monotonic() - Monotonique (ne recule jamais)
start = time.monotonic()
# ... code ...
elapsed = time.monotonic() - start

# process_time() - Temps CPU du processus
start = time.process_time()
# ... code CPU intensif ...
cpu_time = time.process_time() - start

# thread_time() - Temps CPU du thread
start = time.thread_time()
# ... code ...
thread_time = time.thread_time() - start


[OK] CONVERSIONS TIMESTAMP


# Timestamp -> struct_time (local)
timestamp = 1699564800.0
local_time = time.localtime(timestamp)
# time.struct_time(tm_year=2023, tm_mon=11, tm_mday=9, ...)

# Timestamp -> struct_time (UTC)
utc_time = time.gmtime(timestamp)

# struct_time -> Timestamp
timestamp = time.mktime(local_time)

# Timestamp actuel en local
now_local = time.localtime()

# Timestamp actuel en UTC
now_utc = time.gmtime()

# Timestamp sans argument = now
current = time.localtime()  # Équivalent à localtime(time())


[OK] FORMATAGE TEMPS


# strftime - struct_time -> String
t = time.localtime()
formatted = time.strftime("%Y-%m-%d %H:%M:%S", t)
# "2023-11-09 14:30:45"

# strftime sans argument = now
now_str = time.strftime("%Y-%m-%d %H:%M:%S")

# Format ISO 8601
iso_format = time.strftime("%Y-%m-%dT%H:%M:%S", t)
# "2023-11-09T14:30:45"

# Formats courants
date_only = time.strftime("%Y-%m-%d")           # 2023-11-09
time_only = time.strftime("%H:%M:%S")           # 14:30:45
datetime_fr = time.strftime("%d/%m/%Y %H:%M")   # 09/11/2023 14:30
datetime_us = time.strftime("%m/%d/%Y %I:%M %p") # 11/09/2023 02:30 PM

# strptime - String -> struct_time
date_str = "2023-11-09 14:30:45"
parsed = time.strptime(date_str, "%Y-%m-%d %H:%M:%S")

# Exemples parsing
date1 = time.strptime("09/11/2023", "%d/%m/%Y")
date2 = time.strptime("Nov 9, 2023", "%b %d, %Y")
date3 = time.strptime("2023-11-09T14:30:45", "%Y-%m-%dT%H:%M:%S")


[OK] CODES FORMAT strftime/strptime


# Année
%Y    # 2023 (4 chiffres)
%y    # 23 (2 chiffres)

# Mois
%m    # 11 (01-12)
%B    # November (nom complet)
%b    # Nov (abrégé)

# Jour
%d    # 09 (01-31)
%j    # 313 (jour de l'année 001-366)
%A    # Thursday (nom complet)
%a    # Thu (abrégé)
%w    # 4 (0=Dimanche, 6=Samedi)
%u    # 4 (1=Lundi, 7=Dimanche)

# Heure
%H    # 14 (00-23, 24h)
%I    # 02 (01-12, 12h)
%p    # PM (AM/PM)
%M    # 30 (minutes 00-59)
%S    # 45 (secondes 00-59)
%f    # 123456 (microsecondes, pas dans time)

# Timezone
%z    # +0100 (offset UTC)
%Z    # CET (nom timezone)

# Combinaisons
%c    # Thu Nov 9 14:30:45 2023 (locale)
%x    # 11/09/23 (date locale)
%X    # 14:30:45 (heure locale)

# Autres
%%    # % (caractère littéral)


[OK] struct_time ATTRIBUTES


t = time.localtime()

t.tm_year   # 2023 (année)
t.tm_mon    # 11 (mois 1-12)
t.tm_mday   # 9 (jour du mois 1-31)
t.tm_hour   # 14 (heure 0-23)
t.tm_min    # 30 (minutes 0-59)
t.tm_sec    # 45 (secondes 0-61, leap seconds)
t.tm_wday   # 3 (jour semaine 0=Lundi, 6=Dimanche)
t.tm_yday   # 313 (jour de l'année 1-366)
t.tm_isdst  # 0 (DST: -1=inconnu, 0=non, 1=oui)

# Accès par index (déconseillé)
year = t[0]
month = t[1]
day = t[2]

# Tuple complet
tuple_form = tuple(t)
# (2023, 11, 9, 14, 30, 45, 3, 313, 0)


[OK] SLEEP & DELAYS


# Sleep basique
time.sleep(1)           # 1 seconde
time.sleep(0.5)         # 500 millisecondes
time.sleep(0.001)       # 1 milliseconde
time.sleep(1/60)        # ~16.67 ms (60 FPS)

# Sleep avec précision
duration = 0.0001       # 100 microsecondes
time.sleep(duration)

# Sleep interruptible (avec signal handling)
import signal
import time

def handler(signum, frame):
    raise KeyboardInterrupt

signal.signal(signal.SIGINT, handler)

try:
    time.sleep(10)
except KeyboardInterrupt:
    print("Interrupted!")

# Countdown avec affichage
def countdown(seconds):
    for i in range(seconds, 0, -1):
        print(f"\r{i} seconds remaining...", end="", flush=True)
        time.sleep(1)
    print("\rDone!                    ")

# Sleep jusqu'à heure précise
def sleep_until(target_time):
    """Sleep until target timestamp"""
    wait_time = target_time - time.time()
    if wait_time > 0:
        time.sleep(wait_time)


[OK] TIMERS & BENCHMARKS


# Timer simple
class Timer:
    def __init__(self):
        self.start_time = None
        
    def start(self):
        self.start_time = time.perf_counter()
        
    def stop(self):
        elapsed = time.perf_counter() - self.start_time
        return elapsed

# Utilisation
timer = Timer()
timer.start()
# ... code ...
elapsed = timer.stop()
print(f"Elapsed: {elapsed:.4f}s")

# Context manager timer
class TimerContext:
    def __enter__(self):
        self.start = time.perf_counter()
        return self
        
    def __exit__(self, *args):
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.4f}s")

# Utilisation
with TimerContext():
    # ... code ...
    pass

# Décorateur timer
def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timer_decorator
def my_function():
    time.sleep(1)

# Benchmark multiple runs
def benchmark(func, runs=100):
    times = []
    for _ in range(runs):
        start = time.perf_counter()
        func()
        times.append(time.perf_counter() - start)
    
    avg = sum(times) / len(times)
    min_time = min(times)
    max_time = max(times)
    
    print(f"Average: {avg:.6f}s")
    print(f"Min: {min_time:.6f}s")
    print(f"Max: {max_time:.6f}s")


[OK] TIMEZONE & UTC


# Temps local vs UTC
local = time.localtime()
utc = time.gmtime()

# Timezone offset en secondes
offset = time.timezone    # Secondes à l'ouest de UTC
dst_offset = time.altzone # Avec DST

# Timezone name
tz_name = time.tzname     # ('EST', 'EDT') par exemple

# Daylight Saving Time
is_dst = time.daylight    # 1 si DST défini, 0 sinon

# Convertir local -> UTC
local_time = time.localtime()
utc_time = time.gmtime(time.mktime(local_time))

# Convertir UTC -> local
utc_time = time.gmtime()
local_time = time.localtime(time.mktime(utc_time) - time.timezone)

# Timestamp UTC actuel
utc_timestamp = time.time()  # Toujours UTC!


[OK] CALCULS DE TEMPS


# Ajouter/soustraire du temps
now = time.time()
one_hour_later = now + 3600           # +1 heure
one_day_ago = now - 86400             # -1 jour
one_week_later = now + (7 * 86400)    # +7 jours

# Différence entre deux timestamps
start = time.time()
time.sleep(2)
end = time.time()
duration = end - start  # ~2.0

# Calculer durée avec struct_time
t1 = time.strptime("2023-11-09 10:00:00", "%Y-%m-%d %H:%M:%S")
t2 = time.strptime("2023-11-09 15:30:00", "%Y-%m-%d %H:%M:%S")
ts1 = time.mktime(t1)
ts2 = time.mktime(t2)
duration = ts2 - ts1  # 19800.0 secondes (5.5 heures)

# Constantes utiles
SECOND = 1
MINUTE = 60
HOUR = 3600
DAY = 86400
WEEK = 604800

# Exemples
three_days = 3 * DAY
two_weeks = 2 * WEEK


[OK] FORMATAGE DURÉE


def format_duration(seconds):
    """Format seconds into readable duration"""
    if seconds < 60:
        return f"{seconds:.2f}s"
    elif seconds < 3600:
        minutes = seconds / 60
        return f"{minutes:.2f}m"
    elif seconds < 86400:
        hours = seconds / 3600
        return f"{hours:.2f}h"
    else:
        days = seconds / 86400
        return f"{days:.2f}d"

# Format détaillé
def format_duration_detailed(seconds):
    """Format seconds into days, hours, minutes, 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}d")
    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)

# Utilisation
print(format_duration(3725))           # "1.03h"
print(format_duration_detailed(3725))  # "1h 2m 5s"


[OK] TIMESTAMP CONVERSIONS


# Secondes -> autres unités
seconds = 1.5
milliseconds = seconds * 1000        # 1500
microseconds = seconds * 1_000_000   # 1500000
nanoseconds = seconds * 1_000_000_000 # 1500000000

# Autres unités -> secondes
ms = 1500
seconds = ms / 1000  # 1.5

us = 1_500_000
seconds = us / 1_000_000  # 1.5

ns = 1_500_000_000
seconds = ns / 1_000_000_000  # 1.5

# Timestamp readable
timestamp = time.time()
readable = time.ctime(timestamp)
# "Thu Nov  9 14:30:45 2023"

# ctime sans argument = now
now_readable = time.ctime()

# asctime - struct_time -> readable string
t = time.localtime()
readable = time.asctime(t)
# "Thu Nov  9 14:30:45 2023"


[OK] PERFORMANCE TIMING


# Comparer différentes approches
import time

def approach1():
    return sum(range(1000000))

def approach2():
    total = 0
    for i in range(1000000):
        total += i
    return total

# Mesurer
start1 = time.perf_counter()
result1 = approach1()
time1 = time.perf_counter() - start1

start2 = time.perf_counter()
result2 = approach2()
time2 = time.perf_counter() - start2

print(f"Approach 1: {time1:.6f}s")
print(f"Approach 2: {time2:.6f}s")
print(f"Speedup: {time2/time1:.2f}x")

# Warm-up avant mesure
def benchmark_with_warmup(func, warmup=3, runs=10):
    # Warm-up
    for _ in range(warmup):
        func()
    
    # Mesures
    times = []
    for _ in range(runs):
        start = time.perf_counter()
        func()
        times.append(time.perf_counter() - start)
    
    return sum(times) / len(times)


[OK] RATE LIMITING


# Simple rate limiter
class RateLimiter:
    def __init__(self, calls_per_second):
        self.min_interval = 1.0 / calls_per_second
        self.last_call = 0
    
    def wait(self):
        elapsed = time.time() - self.last_call
        if elapsed < self.min_interval:
            time.sleep(self.min_interval - elapsed)
        self.last_call = time.time()

# Utilisation
limiter = RateLimiter(5)  # 5 appels par seconde max

for i in range(10):
    limiter.wait()
    print(f"Call {i+1}")

# Rate limiter avec fenêtre glissante
class SlidingWindowRateLimiter:
    def __init__(self, max_calls, window_seconds):
        self.max_calls = max_calls
        self.window = window_seconds
        self.calls = []
    
    def can_call(self):
        now = time.time()
        # Supprimer appels hors fenêtre
        self.calls = [t for t in self.calls if now - t < self.window]
        return len(self.calls) < self.max_calls
    
    def wait_if_needed(self):
        while not self.can_call():
            time.sleep(0.1)
        self.calls.append(time.time())

# Utilisation: max 10 appels par minute
limiter = SlidingWindowRateLimiter(10, 60)

for i in range(20):
    limiter.wait_if_needed()
    print(f"Call {i+1}")


[OK] RETRY AVEC BACKOFF


# Retry exponentiel
def retry_with_backoff(func, max_retries=5, base_delay=1):
    """Retry function with exponential backoff"""
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            delay = base_delay * (2 ** attempt)
            print(f"Attempt {attempt+1} failed, retrying in {delay}s...")
            time.sleep(delay)

# Utilisation
def flaky_function():
    import random
    if random.random() < 0.7:  # 70% échec
        raise Exception("Failed!")
    return "Success!"

result = retry_with_backoff(flaky_function)

# Retry avec jitter
import random

def retry_with_jitter(func, max_retries=5, base_delay=1):
    for attempt in range(max_retries):
        try:
            return func()
        except Exception as e:
            if attempt == max_retries - 1:
                raise
            # Backoff exponentiel + jitter aléatoire
            delay = base_delay * (2 ** attempt) * (0.5 + random.random())
            print(f"Retry in {delay:.2f}s...")
            time.sleep(delay)


[OK] TIMEOUT IMPLEMENTATION


# Timeout simple
def run_with_timeout(func, timeout_seconds):
    """Run function with timeout (single-threaded)"""
    start = time.time()
    result = None
    
    # Note: Pas un vrai timeout, juste vérification périodique
    # Pour vrai timeout, utiliser threading ou signal
    
    class TimeoutError(Exception):
        pass
    
    def check_timeout():
        if time.time() - start > timeout_seconds:
            raise TimeoutError(f"Function exceeded {timeout_seconds}s")
    
    return func  # Simplification, voir threading pour vrai timeout

# Timeout avec threading
import threading

def timeout_wrapper(timeout_seconds):
    def decorator(func):
        def wrapper(*args, **kwargs):
            result = [None]
            exception = [None]
            
            def target():
                try:
                    result[0] = func(*args, **kwargs)
                except Exception as e:
                    exception[0] = e
            
            thread = threading.Thread(target=target)
            thread.daemon = True
            thread.start()
            thread.join(timeout_seconds)
            
            if thread.is_alive():
                raise TimeoutError(f"Function exceeded {timeout_seconds}s")
            
            if exception[0]:
                raise exception[0]
            
            return result[0]
        return wrapper
    return decorator

@timeout_wrapper(5)
def slow_function():
    time.sleep(10)
    return "Done"


[OK] SCHEDULING & INTERVALS


# Exécuter toutes les N secondes
def run_every(interval, func):
    """Run function every interval seconds"""
    next_run = time.time()
    
    while True:
        current = time.time()
        if current >= next_run:
            func()
            next_run = current + interval
        time.sleep(0.1)  # Petit sleep pour ne pas boucler à 100% CPU

# Exécuter à heure précise
def run_at_time(hour, minute, func):
    """Run function at specific time daily"""
    while True:
        now = time.localtime()
        
        if now.tm_hour == hour and now.tm_min == minute:
            func()
            time.sleep(60)  # Attendre 1 minute pour éviter double exécution
        
        time.sleep(30)  # Vérifier toutes les 30 secondes

# Scheduler simple
class SimpleScheduler:
    def __init__(self):
        self.tasks = []
    
    def every(self, interval, func):
        """Schedule function every interval seconds"""
        self.tasks.append({
            'interval': interval,
            'func': func,
            'next_run': time.time() + interval
        })
    
    def run(self):
        """Run scheduler (blocking)"""
        while True:
            now = time.time()
            for task in self.tasks:
                if now >= task['next_run']:
                    task['func']()
                    task['next_run'] = now + task['interval']
            time.sleep(0.1)

# Utilisation
scheduler = SimpleScheduler()
scheduler.every(5, lambda: print("Every 5s"))
scheduler.every(10, lambda: print("Every 10s"))
# scheduler.run()  # Bloquant


[OK] PROFILING AVEC time


# Profile sections de code
sections = {}

def profile_section(name):
    """Context manager pour profiler sections"""
    class ProfileContext:
        def __enter__(self):
            self.start = time.perf_counter()
            return self
        
        def __exit__(self, *args):
            elapsed = time.perf_counter() - self.start
            if name not in sections:
                sections[name] = []
            sections[name].append(elapsed)
    
    return ProfileContext()

# Utilisation
with profile_section('database_query'):
    time.sleep(0.1)  # Simule query

with profile_section('api_call'):
    time.sleep(0.2)  # Simule API

with profile_section('database_query'):
    time.sleep(0.15)

# Afficher résultats
for name, times in sections.items():
    avg = sum(times) / len(times)
    total = sum(times)
    print(f"{name}: avg={avg:.4f}s, total={total:.4f}s, calls={len(times)}")


[OK] HUMAN-READABLE TIME


# Relative time (ago)
def time_ago(timestamp):
    """Convert timestamp to 'X time ago' format"""
    diff = time.time() - timestamp
    
    if diff < 60:
        return f"{int(diff)}s ago"
    elif diff < 3600:
        return f"{int(diff/60)}m ago"
    elif diff < 86400:
        return f"{int(diff/3600)}h ago"
    elif diff < 604800:
        return f"{int(diff/86400)}d ago"
    elif diff < 2592000:
        return f"{int(diff/604800)}w ago"
    else:
        return f"{int(diff/2592000)}mo ago"

# Utilisation
past = time.time() - 3725
print(time_ago(past))  # "1h ago"

# Future time
def time_until(timestamp):
    """Convert timestamp to 'in X time' format"""
    diff = timestamp - time.time()
    
    if diff < 0:
        return "in the past"
    elif diff < 60:
        return f"in {int(diff)}s"
    elif diff < 3600:
        return f"in {int(diff/60)}m"
    elif diff < 86400:
        return f"in {int(diff/3600)}h"
    else:
        return f"in {int(diff/86400)}d"


[OK] COMPARAISON time vs datetime


# time - Plus bas niveau, timestamps, performance
import time

# Simple et rapide pour timestamps
timestamp = time.time()
time.sleep(1)

# Bon pour benchmarking
start = time.perf_counter()
# code
elapsed = time.perf_counter() - start

# datetime - Plus haut niveau, manipulation dates
from datetime import datetime, timedelta

# Manipulation dates plus intuitive
now = datetime.now()
tomorrow = now + timedelta(days=1)
formatted = now.strftime("%Y-%m-%d")

# Timezone aware
from datetime import timezone
utc_now = datetime.now(timezone.utc)

# [ATTENTION] Ne pas mélanger
# time pour: performance, timestamps, sleep
# datetime pour: dates, calendrier, timezone


[OK] BEST PRACTICES


# [OK] Utiliser perf_counter() pour mesures précises
# [OK] Utiliser monotonic() pour durées longues
# [OK] Utiliser time() pour timestamps simples
# [OK] Toujours stocker UTC, afficher en local
# [OK] Utiliser strptime avec format explicite
# [OK] Préférer datetime pour manipulation dates
# [OK] Context managers pour timing
# [OK] Warm-up avant benchmarks

# [X] Ne pas utiliser time() pour benchmarks (drift)
# [X] Ne pas oublier sleep(0) peut être > 0
# [X] Ne pas supposer précision < milliseconde
# [X] Ne pas calculer dates avec arithmétique simple
# [X] Ne pas ignorer les fuseaux horaires


[OK] PATTERNS COURANTS


# Progress bar avec temps estimé
def progress_bar(total, current, start_time):
    elapsed = time.time() - start_time
    if current > 0:
        rate = elapsed / current
        remaining = (total - current) * rate
        eta = time.strftime("%H:%M:%S", time.gmtime(remaining))
    else:
        eta = "??:??:??"
    
    percent = (current / total) * 100
    bar = "█" * int(percent / 2) + "░" * (50 - int(percent / 2))
    print(f"\r[{bar}] {percent:.1f}% ETA: {eta}", end="", flush=True)

# Utilisation
start = time.time()
total = 100
for i in range(total):
    progress_bar(total, i+1, start)
    time.sleep(0.05)

# Debounce function calls
class Debouncer:
    def __init__(self, wait_seconds):
        self.wait = wait_seconds
        self.last_call = 0
    
    def can_call(self):
        now = time.time()
        if now - self.last_call >= self.wait:
            self.last_call = now
            return True
        return False

# Cache avec expiration
class TimedCache:
    def __init__(self, ttl_seconds):
        self.cache = {}
        self.ttl = ttl_seconds
    
    def set(self, key, value):
        self.cache[key] = (value, time.time())
    
    def get(self, key):
        if key not in self.cache:
            return None
        value, timestamp = self.cache[key]
        if time.time() - timestamp > self.ttl:
            del self.cache[key]
            return None
        return value


[OK] RESSOURCES


# Documentation: https://docs.python.org/3/library/time.html
# timeit pour benchmarks: https://docs.python.org/3/library/timeit.html
# datetime pour dates: https://docs.python.org/3/library/datetime.html
# schedule pour scheduling: https://schedule.readthedocs.io/
# APScheduler: https://apscheduler.readthedocs.io/