diff --git a/index.html b/index.html
index 0636f48..f93ef53 100644
--- a/index.html
+++ b/index.html
@@ -393,6 +393,65 @@
+
+
+
+
diff --git a/settings.css b/settings.css
index ad8bf25..a49f769 100644
--- a/settings.css
+++ b/settings.css
@@ -32,6 +32,35 @@
border-color: #6B8DD6;
}
+.settings-form select {
+ width: 100%;
+ padding: 12px;
+ padding-right: 36px;
+ border-radius: 6px;
+ font-size: 1rem;
+ box-sizing: border-box;
+ border: 1px solid #404040;
+ background-color: #2a2a2a;
+ color: #e0e0e0;
+ appearance: none;
+ -webkit-appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23a0a0a0' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 12px center;
+ cursor: pointer;
+}
+
+.settings-form select:focus {
+ outline: none;
+ border-color: #6B8DD6;
+}
+
+.settings-form select option,
+.settings-form select optgroup {
+ background-color: #2a2a2a;
+ color: #e0e0e0;
+}
+
.settings-btn {
width: 100%;
padding: 12px;
diff --git a/src/dateUtils.ts b/src/dateUtils.ts
new file mode 100644
index 0000000..9ccfb06
--- /dev/null
+++ b/src/dateUtils.ts
@@ -0,0 +1,36 @@
+export interface CurrentDateResult {
+ date: string; // "2026-02-13"
+ dateTime: Date; // new Date(year, month-1, day) — local-midnight Date
+ timeString: string; // "HH:MM"
+}
+
+export async function getCurrentDate(timezone: string): Promise {
+ if (timezone) {
+ try {
+ const response = await fetch(
+ `https://timeapi.io/api/v1/time/current/zone?timezone=${encodeURIComponent(timezone)}`
+ );
+ if (response.ok) {
+ const data = await response.json();
+ // data.date is "YYYY-MM-DD", data.time is "HH:MM:SS.mmm" or "HH:MM"
+ const [year, month, day] = data.date.split('-').map(Number);
+ const dateTime = new Date(year, month - 1, day);
+ const timeParts = data.time.split(':');
+ const timeString = `${timeParts[0].padStart(2, '0')}:${timeParts[1].padStart(2, '0')}`;
+ return { date: data.date, dateTime, timeString };
+ }
+ } catch (e) {
+ console.warn('timeapi.io request failed, falling back to browser time:', e);
+ }
+ }
+
+ // Fallback: browser local time
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = now.getMonth() + 1;
+ const day = now.getDate();
+ const date = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
+ const dateTime = new Date(year, now.getMonth(), day);
+ const timeString = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
+ return { date, dateTime, timeString };
+}
diff --git a/src/index.ts b/src/index.ts
index f047b52..c8da14f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,6 +7,7 @@ import { closeMobileMenu, delay, getCleanName, getIcon, handleMobileCalendarClic
import { showAuthForms, toggleAuthForms, showRegisterForm, showLoginForm, hideAuthForms, closeAuthModal } from './auth';
import { appState } from "./state";
import { getNutritionInfo, NutritionInfo, clearNutritionCache, getCacheStats } from './claudeService';
+import { getCurrentDate } from './dateUtils';
// PWA Service Worker Registration
import { registerSW } from 'virtual:pwa-register';
@@ -42,6 +43,20 @@ document.addEventListener('DOMContentLoaded', async function() {
setupEventListeners();
});
+async function initTimezone() {
+ try {
+ const allDocs = await AppwriteDB.getUserSettings();
+ const globalSettings = allDocs.find((d: any) => !d.goalName);
+ appState.userTimezone = globalSettings?.timezone ?? '';
+ } catch {
+ appState.userTimezone = '';
+ }
+ const result = await getCurrentDate(appState.userTimezone);
+ selectedDate = result.dateTime;
+ currentViewDate = result.dateTime;
+ appState.todayDateString = result.date;
+}
+
async function initializeAuth() {
// Check if this is a shared view first
const urlParams = new URLSearchParams(window.location.search);
@@ -60,6 +75,7 @@ async function initializeAuth() {
if (isLoggedIn) {
currentUser = await AppwriteAuth.getCurrentUser();
await showMainApp();
+ await initTimezone();
selectDate(selectedDate);
} else {
showAuthForms();
@@ -91,6 +107,7 @@ async function handleRegister(e: SubmitEvent) {
closeAuthModal();
await showMainApp();
+ await initTimezone();
selectDate(selectedDate);
swal('Registration successful! Welcome to Food Tracker.');
@@ -117,6 +134,7 @@ async function handleLogin(e: SubmitEvent) {
closeAuthModal();
await showMainApp();
+ await initTimezone();
selectDate(selectedDate);
} catch (error) {
@@ -192,12 +210,14 @@ async function handleSaveSettings(e: SubmitEvent) {
const height = getInputById('height').value;
const bmi = getInputById('bmi').value;
const bmiResult = getInputById('bmiResult');
+ const timezone = getInputById('timezone').value;
const metricsData = {
bodyWeight: bodyWeight ? parseFloat(bodyWeight) : undefined,
height: height ? parseFloat(height) : undefined,
bmi: bmi ? parseFloat(bmi) : undefined,
bmiResult: bmiResult ? bmiResult.value : undefined,
+ timezone: timezone || undefined,
};
// Find existing global settings doc (no goalName) and update it, or create new one
@@ -210,6 +230,7 @@ async function handleSaveSettings(e: SubmitEvent) {
await AppwriteDB.saveUserSettings(metricsData);
}
+ appState.userTimezone = timezone;
hideLoading();
toggleSettingsView();
selectDate(selectedDate);
@@ -308,11 +329,13 @@ async function toggleSettingsView() {
getInputById('height').value = globalSettings.height ?? '';
getInputById('bmi').value = globalSettings.bmi ?? '';
getInputById('bmiResult').value = globalSettings.bmiResult ?? '';
+ (document.getElementById('timezone') as HTMLSelectElement).value = globalSettings.timezone ?? '';
} else {
getInputById('bodyWeight').value = '';
getInputById('height').value = '';
getInputById('bmi').value = '';
getInputById('bmiResult').value = '';
+ (document.getElementById('timezone') as HTMLSelectElement).value = '';
}
const goals: UserSettings[] = allDocs
@@ -1331,11 +1354,7 @@ const addFood = async () => {
fiber: proportion.info.fiber,
date: date.split('T')[0],
alkaline: selectedFood.info.alkaline,
- time: new Date().toLocaleTimeString('en-US', {
- hour12: false,
- hour: '2-digit',
- minute: '2-digit'
- })
+ time: (await getCurrentDate(appState.userTimezone)).timeString
};
// Save to Appwrite
@@ -1961,7 +1980,7 @@ function createDayElement(day: number, isOtherMonth: boolean, year: number, mont
}
// Check if this is today
- const today = new Date().toISOString().split('T')[0];
+ const today = appState.todayDateString || new Date().toISOString().split('T')[0];
if (!isOtherMonth && dateString === today) {
dayElement.classList.add('today');
}
diff --git a/src/state.ts b/src/state.ts
index 10c422d..c3feafa 100644
--- a/src/state.ts
+++ b/src/state.ts
@@ -7,6 +7,8 @@ interface AppState {
calendarMonthlyCalories: DailyTotalCalories[];
isSharedView: boolean;
sharedData: SharedDay | null;
+ userTimezone: string;
+ todayDateString: string;
}
export const appState: AppState = {
@@ -15,5 +17,7 @@ export const appState: AppState = {
searchResults: [],
calendarMonthlyCalories: [],
isSharedView: false,
- sharedData: null
+ sharedData: null,
+ userTimezone: '',
+ todayDateString: ''
};
diff --git a/src/types.ts b/src/types.ts
index 7e720cf..240b10a 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -33,6 +33,7 @@ export type UserSettings = {
bmiResult?: string;
goalName?: string;
isActive?: boolean;
+ timezone?: string;
};
export type DailyTotalCalories = {