How Zakirullin Files Handles Timezones for Journals: A Technical Deep Dive
Zakirullin Files stores a per-user timezone in config.json and applies it consistently to journal filenames, daily headers, and entry timestamps, falling back to UTC when the setting is invalid or empty.
Zakirullin Files is a Telegram-based journaling system that organizes entries as markdown files. To ensure accurate chronological organization across different geographic regions, the repository implements comprehensive timezone-aware journaling that governs file naming, header generation, and timestamp formatting based on individual user preferences stored in server/userconfig/userconfig.go.
Per-User Timezone Configuration
The timezone architecture centers on the Config struct, which provides thread-safe access to user-specific settings. Each user has a dedicated configuration file where the preferred IANA timezone identifier is persisted.
Reading the Timezone Setting
The Timezone() method retrieves the stored identifier and returns a *time.Location instance validated against Go's standard IANA database. If the configuration field is empty or the identifier is invalid, the method safely defaults to UTC.
// server/userconfig/userconfig.go:90-103
func (c *Config) Timezone() *time.Location {
cfg, _ := c.read(c.filename)
if cfg.Timezone == "" {
return time.UTC
}
location, err := time.LoadLocation(cfg.Timezone)
if err != nil {
return time.UTC
}
return location
}
Persisting Timezone Changes via Bot UI
Users modify their timezone through an inline keyboard interface in the Telegram bot. When a user selects a new timezone, the bot handler extracts the IANA identifier and delegates persistence to the SetTimezone method.
// server/bot.go
func (b *Bot) setTimezone(params []string) error {
timezone := params[0] // e.g. "America/New_York"
if err := b.cfg.SetTimezone(timezone); err != nil {
return fmt.Errorf("setTimezone : %w", err)
}
return b.showTimezone(nil) // refresh the UI
}
The SetTimezone method acquires a user-specific mutex before writing to prevent race conditions during concurrent updates.
// server/userconfig/userconfig.go:105-119
func (c *Config) SetTimezone(tz string) error {
lock := c.userLock()
lock.Lock()
defer lock.Unlock()
cfg, err := c.read(c.filename)
if err != nil { … }
cfg.Timezone = tz
return c.write(cfg)
}
Timezone-Aware Journal Operations
The server/journal/journal.go package consumes the validated *time.Location to localize all temporal metadata. Every operation that involves dates or times first converts the instant to the user's zone using time.Time.In(timezone).
Date-Based Filename Generation
Journal files are organized by local date using the todayJournalFilename function. This ensures that a user in Tokyo receives a different filename than a user in London when journaling simultaneously, preventing cross-date entry mixing.
// server/journal/journal.go:28-30
func todayJournalFilename(timezone *time.Location) string {
return Now().In(timezone).Format("2006.01 January.md")
}
Localized Daily Headers
When initializing a new day's journal, todayHeader generates a markdown heading that reflects the day, month, and weekday according to the user's local calendar.
// server/journal/journal.go:32-35
func todayHeader(timezone *time.Location) string {
nowTZ := Now().In(timezone)
return fmt.Sprintf("## %d %s, %s",
nowTZ.Day(), nowTZ.Format("January"), nowTZ.Weekday())
}
Entry Timestamp Formatting
Both AddRecord and AddEmoji format timestamps by first converting the current instant to the user's timezone. This ensures that every journal entry displays the correct local time regardless of the server's system clock.
// server/journal/journal.go:58-66
timestamp := Now().In(timezone).Format("`15:04`")
if txt.HasImage(record) { … } else {
record = fmt.Sprintf("%s %s\n", timestamp, record)
}
End-to-End Message Flow
The bot orchestrates timezone consistency through server/bot.go by retrieving the user's location at the entry point and propagating it through the call stack. When processing an incoming message, the bot fetches the timezone via b.cfg.Timezone() and passes it to both the chat rendering logic and the journal persistence layer.
// server/bot.go (excerpt)
msgHash, err := b.appendToChat(msg, b.cfg.Timezone())
// …
err := journal.AddRecord(b.fs, content, b.cfg.Timezone())
This design ensures that the filename, daily header, and inline timestamp within a single journal entry all reference the same temporal frame of reference.
Summary
- Zakirullin Files maintains per-user timezone settings in
server/userconfig/userconfig.go, safely defaulting to UTC when thetimezonefield is empty or invalid. - The
Timezone()method validates IANA identifiers usingtime.LoadLocationand returns a*time.Locationfor consistent time conversion. - Journal filenames reflect local dates via
todayJournalFilenameinserver/journal/journal.go, ensuring entries are filed under the correct daily document. - All timestamps are localized using
time.Time.In(timezone)before formatting inAddRecordandAddEmoji. - The
SetTimezonemethod provides thread-safe updates through the bot UI, persisting changes to the user'sconfig.jsonfile.
Frequently Asked Questions
What is the default timezone in Zakirullin Files?
If the timezone field in config.json is empty or contains an invalid IANA identifier, the system defaults to UTC. This fallback is hardcoded in the Timezone() method in server/userconfig/userconfig.go to guarantee the application always operates with a valid *time.Location regardless of configuration state.
How do I change my timezone in Zakirullin Files?
Users can update their timezone through the "Timezone" button in the Telegram bot interface. This triggers the setTimezone handler in server/bot.go, which calls Config.SetTimezone to write the new IANA identifier (such as "Europe/Berlin" or "Pacific/Auckland") to the configuration file.
Does Zakirullin Files handle daylight saving time transitions?
Yes. By utilizing standard IANA timezone identifiers (e.g., "America/New_York" rather than fixed offsets) and Go's time.LoadLocation function, Zakirullin Files automatically respects daylight saving time rules. The system calculates the correct offset based on the specific date and region without requiring manual clock adjustments.
Where does Zakirullin Files store timezone settings?
Per-user timezone settings are stored in individual config.json files managed by server/userconfig/userconfig.go. The Timezone field is read on every journal operation—including filename generation, header creation, and timestamp formatting—to ensure complete consistency with the user's selected locale.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →