arcticaurora commited on
Commit
5b483a6
·
verified ·
1 Parent(s): 8b74067

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +176 -0
app.py ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException, Body
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+
4
+
5
+ from pydantic import BaseModel, Field
6
+ from datetime import datetime, timezone
7
+ from typing import Literal
8
+ import pytz
9
+ from dateutil import parser as dateutil_parser
10
+
11
+ app = FastAPI(
12
+ title="Secure Time Utilities API",
13
+ version="1.0.0",
14
+ description="Provides secure UTC/local time retrieval, formatting, timezone conversion, and comparison.",
15
+ )
16
+
17
+
18
+ origins = ["*"]
19
+
20
+ app.add_middleware(
21
+ CORSMiddleware,
22
+ allow_origins=origins,
23
+ allow_credentials=True,
24
+ allow_methods=["*"],
25
+ allow_headers=["*"],
26
+ )
27
+
28
+
29
+ # -------------------------------
30
+ # Pydantic models
31
+ # -------------------------------
32
+
33
+
34
+ class FormatTimeInput(BaseModel):
35
+ format: str = Field(
36
+ "%Y-%m-%d %H:%M:%S", description="Python strftime format string"
37
+ )
38
+ timezone: str = Field(
39
+ "UTC", description="IANA timezone name (e.g., UTC, America/New_York)"
40
+ )
41
+
42
+
43
+ class ConvertTimeInput(BaseModel):
44
+ timestamp: str = Field(
45
+ ..., description="ISO 8601 formatted time string (e.g., 2024-01-01T12:00:00Z)"
46
+ )
47
+ from_tz: str = Field(
48
+ ..., description="Original IANA time zone of input (e.g. UTC or Europe/Berlin)"
49
+ )
50
+ to_tz: str = Field(..., description="Target IANA time zone to convert to")
51
+
52
+
53
+ class ElapsedTimeInput(BaseModel):
54
+ start: str = Field(..., description="Start timestamp in ISO 8601 format")
55
+ end: str = Field(..., description="End timestamp in ISO 8601 format")
56
+ units: Literal["seconds", "minutes", "hours", "days"] = Field(
57
+ "seconds", description="Unit for elapsed time"
58
+ )
59
+
60
+
61
+ class ParseTimestampInput(BaseModel):
62
+ timestamp: str = Field(
63
+ ..., description="Flexible input timestamp string (e.g., 2024-06-01 12:00 PM)"
64
+ )
65
+ timezone: str = Field(
66
+ "UTC", description="Assumed timezone if none is specified in input"
67
+ )
68
+
69
+
70
+ # -------------------------------
71
+ # Routes
72
+ # -------------------------------
73
+
74
+
75
+ @app.get("/get_current_utc_time", summary="Current UTC time")
76
+ def get_current_utc():
77
+ """
78
+ Returns the current time in UTC in ISO format.
79
+ """
80
+ return {"utc": datetime.utcnow().replace(tzinfo=timezone.utc).isoformat()}
81
+
82
+
83
+ @app.get("/get_current_local_time", summary="Current Local Time")
84
+ def get_current_local():
85
+ """
86
+ Returns the current time in local timezone in ISO format.
87
+ """
88
+ return {"local_time": datetime.now().isoformat()}
89
+
90
+
91
+ @app.post("/format_time", summary="Format current time")
92
+ def format_current_time(data: FormatTimeInput):
93
+ """
94
+ Return the current time formatted for a specific timezone and format.
95
+ """
96
+ try:
97
+ tz = pytz.timezone(data.timezone)
98
+ except Exception:
99
+ raise HTTPException(
100
+ status_code=400, detail=f"Invalid timezone: {data.timezone}"
101
+ )
102
+ now = datetime.now(tz)
103
+ try:
104
+ return {"formatted_time": now.strftime(data.format)}
105
+ except Exception as e:
106
+ raise HTTPException(status_code=400, detail=f"Invalid format string: {e}")
107
+
108
+
109
+ @app.post("/convert_time", summary="Convert between timezones")
110
+ def convert_time(data: ConvertTimeInput):
111
+ """
112
+ Convert a timestamp from one timezone to another.
113
+ """
114
+ try:
115
+ from_zone = pytz.timezone(data.from_tz)
116
+ to_zone = pytz.timezone(data.to_tz)
117
+ except Exception as e:
118
+ raise HTTPException(status_code=400, detail=f"Invalid timezone: {e}")
119
+
120
+ try:
121
+ dt = dateutil_parser.parse(data.timestamp)
122
+ if dt.tzinfo is None:
123
+ dt = from_zone.localize(dt)
124
+ else:
125
+ dt = dt.astimezone(from_zone)
126
+ converted = dt.astimezone(to_zone)
127
+ return {"converted_time": converted.isoformat()}
128
+ except Exception as e:
129
+ raise HTTPException(status_code=400, detail=f"Invalid timestamp: {e}")
130
+
131
+
132
+ @app.post("/elapsed_time", summary="Time elapsed between timestamps")
133
+ def elapsed_time(data: ElapsedTimeInput):
134
+ """
135
+ Calculate the difference between two timestamps in chosen units.
136
+ """
137
+ try:
138
+ start_dt = dateutil_parser.parse(data.start)
139
+ end_dt = dateutil_parser.parse(data.end)
140
+ delta = end_dt - start_dt
141
+ except Exception as e:
142
+ raise HTTPException(status_code=400, detail=f"Invalid timestamps: {e}")
143
+
144
+ seconds = delta.total_seconds()
145
+ result = {
146
+ "seconds": seconds,
147
+ "minutes": seconds / 60,
148
+ "hours": seconds / 3600,
149
+ "days": seconds / 86400,
150
+ }
151
+
152
+ return {"elapsed": result[data.units], "unit": data.units}
153
+
154
+
155
+ @app.post("/parse_timestamp", summary="Parse and normalize timestamps")
156
+ def parse_timestamp(data: ParseTimestampInput):
157
+ """
158
+ Parse human-friendly input timestamp and return standardized UTC ISO time.
159
+ """
160
+ try:
161
+ tz = pytz.timezone(data.timezone)
162
+ dt = dateutil_parser.parse(data.timestamp)
163
+ if dt.tzinfo is None:
164
+ dt = tz.localize(dt)
165
+ dt_utc = dt.astimezone(pytz.utc)
166
+ return {"utc": dt_utc.isoformat()}
167
+ except Exception as e:
168
+ raise HTTPException(status_code=400, detail=f"Could not parse: {e}")
169
+
170
+
171
+ @app.get("/list_time_zones", summary="All valid time zones")
172
+ def list_time_zones():
173
+ """
174
+ Return a list of all valid IANA time zones.
175
+ """
176
+ return pytz.all_timezones