timezone.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package timezone // import "miniflux.app/v2/internal/timezone"
  4. import (
  5. "sync"
  6. "time"
  7. )
  8. var (
  9. tzCache = sync.Map{} // Cache for time locations to avoid loading them multiple times.
  10. )
  11. // Convert converts provided date time to actual timezone.
  12. func Convert(tz string, t time.Time) time.Time {
  13. userTimezone := getLocation(tz)
  14. if t.Location().String() == "" {
  15. if t.Before(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)) {
  16. return time.Date(0, time.January, 1, 0, 0, 0, 0, userTimezone)
  17. }
  18. // In this case, the provided date is already converted to the user timezone by Postgres,
  19. // but the timezone information is not set in the time struct.
  20. // We cannot use time.In() because the date will be converted a second time.
  21. return time.Date(
  22. t.Year(),
  23. t.Month(),
  24. t.Day(),
  25. t.Hour(),
  26. t.Minute(),
  27. t.Second(),
  28. t.Nanosecond(),
  29. userTimezone,
  30. )
  31. } else if t.Location() != userTimezone {
  32. return t.In(userTimezone)
  33. }
  34. return t
  35. }
  36. // Now returns the current time with the given timezone.
  37. func Now(tz string) time.Time {
  38. return time.Now().In(getLocation(tz))
  39. }
  40. func getLocation(tz string) *time.Location {
  41. if loc, ok := tzCache.Load(tz); ok {
  42. return loc.(*time.Location)
  43. }
  44. loc, err := time.LoadLocation(tz)
  45. if err != nil {
  46. loc = time.Local
  47. }
  48. tzCache.Store(tz, loc)
  49. return loc
  50. }