diff --git a/docs/exercises.md b/docs/exercises.md index 7a88f72..db87686 100644 --- a/docs/exercises.md +++ b/docs/exercises.md @@ -68,6 +68,7 @@ title: Exercises
  • 107_image_processing_utility - Image processing (Hard)
  • 108_basic_key_value_store - Key-value store (Hard)
  • 109_epoch - Time conversion (Beginner)
  • +
  • 120_time - Time functions exercise (Beginner)
  • diff --git a/internal/exercises/catalog.yaml b/internal/exercises/catalog.yaml index f60c3bd..7488e0e 100644 --- a/internal/exercises/catalog.yaml +++ b/internal/exercises/catalog.yaml @@ -212,6 +212,14 @@ projects: - "Use sort.Sort() to sort slices with your custom comparison logic" - "Remember to make copies of slices to avoid modifying the original" +- slug: 120_time + title: "Time" + description: "Practice working with Go’s time package: formatting dates and computing day differences." + template: "templates/time.go" + test: "templates/time_test.go" + hints: + - "Use time.Format with layout \"2006-01-02\"" + - "Subtract two times to get a Duration, then convert to hours and divide by 24" - slug: 38_time_formatting title: "Time Formatting" difficulty: beginner diff --git a/internal/exercises/solutions/120_time/time.go b/internal/exercises/solutions/120_time/time.go new file mode 100644 index 0000000..d3afb06 --- /dev/null +++ b/internal/exercises/solutions/120_time/time.go @@ -0,0 +1,50 @@ +package time + +import "time" + +// FormatDate formats a time.Time into "YYYY-MM-DD". +func FormatDate(t time.Time) string { + return t.Format("2006-01-02") +} + +// FormatDateTime formats a time.Time into "YYYY-MM-DD HH:MM:SS". +func FormatDateTime(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +} + +// DaysBetween returns the number of days between two dates. +// Positive if end > start, negative if end < start. +// +// Note: This implementation divides the duration by 24 hours. +// When dates span a daylight saving time (DST) transition, the result +// may be off by ±1 hour. For exact day counts across DST, consider +// normalizing both dates to UTC or comparing date components at midnight. +func DaysBetween(start, end time.Time) int { + return int(end.Sub(start).Hours() / 24) +} + + +// AddDays adds a specified number of days to a date and returns the new date. +func AddDays(t time.Time, days int) time.Time { + return t.AddDate(0, 0, days) +} + +// ParseDate parses a string in "YYYY-MM-DD" format into a time.Time. +// Returns zero time if parsing fails. +func ParseDate(s string) time.Time { + t, err := time.Parse("2006-01-02", s) + if err != nil { + return time.Time{} + } + return t +} + +// ParseDateTime parses a string in "YYYY-MM-DD HH:MM:SS" format into a time.Time. +// Returns zero time if parsing fails. +func ParseDateTime(s string) time.Time { + t, err := time.Parse("2006-01-02 15:04:05", s) + if err != nil { + return time.Time{} + } + return t +} diff --git a/internal/exercises/templates/120_time/time.go b/internal/exercises/templates/120_time/time.go new file mode 100644 index 0000000..2c80291 --- /dev/null +++ b/internal/exercises/templates/120_time/time.go @@ -0,0 +1,44 @@ +package timeutils + +import "time" + +// FormatDate formats a time.Time into "YYYY-MM-DD". +func FormatDate(t time.Time) string { + return t.Format("2006-01-02") +} + +// FormatDateTime formats a time.Time into "YYYY-MM-DD HH:MM:SS". +func FormatDateTime(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +} + +// DaysBetween returns the number of days between two dates. +// Positive if end > start, negative if end < start. +func DaysBetween(start, end time.Time) int { + return int(end.Sub(start).Hours() / 24) +} + +// AddDays adds a specified number of days to a date and returns the new date. +func AddDays(t time.Time, days int) time.Time { + return t.AddDate(0, 0, days) +} + +// ParseDate parses a string in "YYYY-MM-DD" format into a time.Time. +// Returns zero time if parsing fails. +func ParseDate(s string) time.Time { + t, err := time.Parse("2006-01-02", s) + if err != nil { + return time.Time{} + } + return t +} + +// ParseDateTime parses a string in "YYYY-MM-DD HH:MM:SS" format into a time.Time. +// Returns zero time if parsing fails. +func ParseDateTime(s string) time.Time { + t, err := time.Parse("2006-01-02 15:04:05", s) + if err != nil { + return time.Time{} + } + return t +} diff --git a/internal/exercises/templates/120_time/time_test.go b/internal/exercises/templates/120_time/time_test.go new file mode 100644 index 0000000..0624d76 --- /dev/null +++ b/internal/exercises/templates/120_time/time_test.go @@ -0,0 +1,82 @@ +package timeutils_test + +import ( + "testing" + "time" + + timeutils "github.com/zhravan/golearn/internal/exercises/templates/120_time" +) + +// Helper to quickly create a date +func mustDate(year int, month time.Month, day int) time.Time { + return time.Date(year, month, day, 0, 0, 0, 0, time.UTC) +} + +// --- Tests for FormatDate --- +func TestFormatDate(t *testing.T) { + d := mustDate(2025, time.October, 2) + want := "2025-10-02" + got := timeutils.FormatDate(d) + if got != want { + t.Fatalf("FormatDate(%v) = %q; want %q", d, got, want) + } +} + +// --- Tests for FormatDateTime --- +func TestFormatDateTime(t *testing.T) { + d := mustDate(2025, time.October, 2) + want := "2025-10-02 00:00:00" + got := timeutils.FormatDateTime(d) + if got != want { + t.Fatalf("FormatDateTime(%v) = %q; want %q", d, got, want) + } +} + +// --- Tests for DaysBetween --- +func TestDaysBetween(t *testing.T) { + start := mustDate(2025, time.October, 1) + end := mustDate(2025, time.October, 11) + + got := timeutils.DaysBetween(start, end) + want := 10 + if got != want { + t.Fatalf("DaysBetween(%v, %v) = %d; want %d", start, end, got, want) + } + + // negative difference + got = timeutils.DaysBetween(end, start) + want = -10 + if got != want { + t.Fatalf("DaysBetween(%v, %v) = %d; want %d", end, start, got, want) + } +} + +// --- Tests for AddDays --- +func TestAddDays(t *testing.T) { + d := mustDate(2025, time.October, 1) + got := timeutils.AddDays(d, 10) + want := mustDate(2025, time.October, 11) + if !got.Equal(want) { + t.Fatalf("AddDays(%v, 10) = %v; want %v", d, got, want) + } +} + +// --- Tests for ParseDate --- +func TestParseDate(t *testing.T) { + input := "2025-10-02" + want := mustDate(2025, time.October, 2) + got := timeutils.ParseDate(input) + if !got.Equal(want) { + t.Fatalf("ParseDate(%q) = %v; want %v", input, got, want) + } +} + +// --- Tests for ParseDateTime --- +func TestParseDateTime(t *testing.T) { + input := "2025-10-02 00:00:00" + want := mustDate(2025, time.October, 2) + got := timeutils.ParseDateTime(input) + if !got.Equal(want) { + t.Fatalf("ParseDateTime(%q) = %v; want %v", input, got, want) + } +}