timezone.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  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. }
  51. // AvailableTimezones returns a map of supported timezones.
  52. // This list is taken from Postgres on Debian Trixie.
  53. func AvailableTimezones() map[string]string {
  54. timezones := []string{
  55. "Africa/Abidjan",
  56. "Africa/Accra",
  57. "Africa/Addis_Ababa",
  58. "Africa/Algiers",
  59. "Africa/Asmara",
  60. "Africa/Bamako",
  61. "Africa/Bangui",
  62. "Africa/Banjul",
  63. "Africa/Bissau",
  64. "Africa/Blantyre",
  65. "Africa/Brazzaville",
  66. "Africa/Bujumbura",
  67. "Africa/Cairo",
  68. "Africa/Casablanca",
  69. "Africa/Ceuta",
  70. "Africa/Conakry",
  71. "Africa/Dakar",
  72. "Africa/Dar_es_Salaam",
  73. "Africa/Djibouti",
  74. "Africa/Douala",
  75. "Africa/El_Aaiun",
  76. "Africa/Freetown",
  77. "Africa/Gaborone",
  78. "Africa/Harare",
  79. "Africa/Johannesburg",
  80. "Africa/Juba",
  81. "Africa/Kampala",
  82. "Africa/Khartoum",
  83. "Africa/Kigali",
  84. "Africa/Kinshasa",
  85. "Africa/Lagos",
  86. "Africa/Libreville",
  87. "Africa/Lome",
  88. "Africa/Luanda",
  89. "Africa/Lubumbashi",
  90. "Africa/Lusaka",
  91. "Africa/Malabo",
  92. "Africa/Maputo",
  93. "Africa/Maseru",
  94. "Africa/Mbabane",
  95. "Africa/Mogadishu",
  96. "Africa/Monrovia",
  97. "Africa/Nairobi",
  98. "Africa/Ndjamena",
  99. "Africa/Niamey",
  100. "Africa/Nouakchott",
  101. "Africa/Ouagadougou",
  102. "Africa/Porto-Novo",
  103. "Africa/Sao_Tome",
  104. "Africa/Timbuktu",
  105. "Africa/Tripoli",
  106. "Africa/Tunis",
  107. "Africa/Windhoek",
  108. "America/Adak",
  109. "America/Anchorage",
  110. "America/Anguilla",
  111. "America/Antigua",
  112. "America/Araguaina",
  113. "America/Argentina/Buenos_Aires",
  114. "America/Argentina/Catamarca",
  115. "America/Argentina/Cordoba",
  116. "America/Argentina/Jujuy",
  117. "America/Argentina/La_Rioja",
  118. "America/Argentina/Mendoza",
  119. "America/Argentina/Rio_Gallegos",
  120. "America/Argentina/Salta",
  121. "America/Argentina/San_Juan",
  122. "America/Argentina/San_Luis",
  123. "America/Argentina/Tucuman",
  124. "America/Argentina/Ushuaia",
  125. "America/Aruba",
  126. "America/Asuncion",
  127. "America/Atikokan",
  128. "America/Atka",
  129. "America/Bahia",
  130. "America/Bahia_Banderas",
  131. "America/Barbados",
  132. "America/Belem",
  133. "America/Belize",
  134. "America/Blanc-Sablon",
  135. "America/Boa_Vista",
  136. "America/Bogota",
  137. "America/Boise",
  138. "America/Cambridge_Bay",
  139. "America/Campo_Grande",
  140. "America/Cancun",
  141. "America/Caracas",
  142. "America/Cayenne",
  143. "America/Cayman",
  144. "America/Chicago",
  145. "America/Chihuahua",
  146. "America/Ciudad_Juarez",
  147. "America/Coral_Harbour",
  148. "America/Costa_Rica",
  149. "America/Coyhaique",
  150. "America/Creston",
  151. "America/Cuiaba",
  152. "America/Curacao",
  153. "America/Danmarkshavn",
  154. "America/Dawson",
  155. "America/Dawson_Creek",
  156. "America/Denver",
  157. "America/Detroit",
  158. "America/Dominica",
  159. "America/Edmonton",
  160. "America/Eirunepe",
  161. "America/El_Salvador",
  162. "America/Ensenada",
  163. "America/Fortaleza",
  164. "America/Fort_Nelson",
  165. "America/Glace_Bay",
  166. "America/Goose_Bay",
  167. "America/Grand_Turk",
  168. "America/Grenada",
  169. "America/Guadeloupe",
  170. "America/Guatemala",
  171. "America/Guayaquil",
  172. "America/Guyana",
  173. "America/Halifax",
  174. "America/Havana",
  175. "America/Hermosillo",
  176. "America/Indiana/Indianapolis",
  177. "America/Indiana/Knox",
  178. "America/Indiana/Marengo",
  179. "America/Indiana/Petersburg",
  180. "America/Indiana/Tell_City",
  181. "America/Indiana/Vevay",
  182. "America/Indiana/Vincennes",
  183. "America/Indiana/Winamac",
  184. "America/Inuvik",
  185. "America/Iqaluit",
  186. "America/Jamaica",
  187. "America/Juneau",
  188. "America/Kentucky/Louisville",
  189. "America/Kentucky/Monticello",
  190. "America/Kralendijk",
  191. "America/La_Paz",
  192. "America/Lima",
  193. "America/Los_Angeles",
  194. "America/Lower_Princes",
  195. "America/Maceio",
  196. "America/Managua",
  197. "America/Manaus",
  198. "America/Marigot",
  199. "America/Martinique",
  200. "America/Matamoros",
  201. "America/Mazatlan",
  202. "America/Menominee",
  203. "America/Merida",
  204. "America/Metlakatla",
  205. "America/Mexico_City",
  206. "America/Miquelon",
  207. "America/Moncton",
  208. "America/Monterrey",
  209. "America/Montevideo",
  210. "America/Montreal",
  211. "America/Montserrat",
  212. "America/Nassau",
  213. "America/New_York",
  214. "America/Nipigon",
  215. "America/Nome",
  216. "America/Noronha",
  217. "America/North_Dakota/Beulah",
  218. "America/North_Dakota/Center",
  219. "America/North_Dakota/New_Salem",
  220. "America/Nuuk",
  221. "America/Ojinaga",
  222. "America/Panama",
  223. "America/Pangnirtung",
  224. "America/Paramaribo",
  225. "America/Phoenix",
  226. "America/Port-au-Prince",
  227. "America/Porto_Acre",
  228. "America/Port_of_Spain",
  229. "America/Porto_Velho",
  230. "America/Puerto_Rico",
  231. "America/Punta_Arenas",
  232. "America/Rainy_River",
  233. "America/Rankin_Inlet",
  234. "America/Recife",
  235. "America/Regina",
  236. "America/Resolute",
  237. "America/Rio_Branco",
  238. "America/Santa_Isabel",
  239. "America/Santarem",
  240. "America/Santiago",
  241. "America/Santo_Domingo",
  242. "America/Sao_Paulo",
  243. "America/Scoresbysund",
  244. "America/Shiprock",
  245. "America/Sitka",
  246. "America/St_Barthelemy",
  247. "America/St_Johns",
  248. "America/St_Kitts",
  249. "America/St_Lucia",
  250. "America/St_Thomas",
  251. "America/St_Vincent",
  252. "America/Swift_Current",
  253. "America/Tegucigalpa",
  254. "America/Thule",
  255. "America/Thunder_Bay",
  256. "America/Tijuana",
  257. "America/Toronto",
  258. "America/Tortola",
  259. "America/Vancouver",
  260. "America/Virgin",
  261. "America/Whitehorse",
  262. "America/Winnipeg",
  263. "America/Yakutat",
  264. "America/Yellowknife",
  265. "Antarctica/Casey",
  266. "Antarctica/Davis",
  267. "Antarctica/DumontDUrville",
  268. "Antarctica/Macquarie",
  269. "Antarctica/Mawson",
  270. "Antarctica/McMurdo",
  271. "Antarctica/Palmer",
  272. "Antarctica/Rothera",
  273. "Antarctica/Syowa",
  274. "Antarctica/Troll",
  275. "Antarctica/Vostok",
  276. "Arctic/Longyearbyen",
  277. "Asia/Aden",
  278. "Asia/Almaty",
  279. "Asia/Amman",
  280. "Asia/Anadyr",
  281. "Asia/Aqtau",
  282. "Asia/Aqtobe",
  283. "Asia/Ashgabat",
  284. "Asia/Atyrau",
  285. "Asia/Baghdad",
  286. "Asia/Bahrain",
  287. "Asia/Baku",
  288. "Asia/Bangkok",
  289. "Asia/Barnaul",
  290. "Asia/Beirut",
  291. "Asia/Bishkek",
  292. "Asia/Brunei",
  293. "Asia/Chita",
  294. "Asia/Chongqing",
  295. "Asia/Colombo",
  296. "Asia/Damascus",
  297. "Asia/Dhaka",
  298. "Asia/Dili",
  299. "Asia/Dubai",
  300. "Asia/Dushanbe",
  301. "Asia/Famagusta",
  302. "Asia/Gaza",
  303. "Asia/Harbin",
  304. "Asia/Hebron",
  305. "Asia/Ho_Chi_Minh",
  306. "Asia/Hong_Kong",
  307. "Asia/Hovd",
  308. "Asia/Irkutsk",
  309. "Asia/Istanbul",
  310. "Asia/Jakarta",
  311. "Asia/Jayapura",
  312. "Asia/Jerusalem",
  313. "Asia/Kabul",
  314. "Asia/Kamchatka",
  315. "Asia/Karachi",
  316. "Asia/Kashgar",
  317. "Asia/Kathmandu",
  318. "Asia/Khandyga",
  319. "Asia/Kolkata",
  320. "Asia/Krasnoyarsk",
  321. "Asia/Kuala_Lumpur",
  322. "Asia/Kuching",
  323. "Asia/Kuwait",
  324. "Asia/Macau",
  325. "Asia/Magadan",
  326. "Asia/Makassar",
  327. "Asia/Manila",
  328. "Asia/Muscat",
  329. "Asia/Nicosia",
  330. "Asia/Novokuznetsk",
  331. "Asia/Novosibirsk",
  332. "Asia/Omsk",
  333. "Asia/Oral",
  334. "Asia/Phnom_Penh",
  335. "Asia/Pontianak",
  336. "Asia/Pyongyang",
  337. "Asia/Qatar",
  338. "Asia/Qostanay",
  339. "Asia/Qyzylorda",
  340. "Asia/Riyadh",
  341. "Asia/Sakhalin",
  342. "Asia/Samarkand",
  343. "Asia/Seoul",
  344. "Asia/Shanghai",
  345. "Asia/Singapore",
  346. "Asia/Srednekolymsk",
  347. "Asia/Taipei",
  348. "Asia/Tashkent",
  349. "Asia/Tbilisi",
  350. "Asia/Tehran",
  351. "Asia/Tel_Aviv",
  352. "Asia/Thimphu",
  353. "Asia/Tokyo",
  354. "Asia/Tomsk",
  355. "Asia/Ulaanbaatar",
  356. "Asia/Urumqi",
  357. "Asia/Ust-Nera",
  358. "Asia/Vientiane",
  359. "Asia/Vladivostok",
  360. "Asia/Yakutsk",
  361. "Asia/Yangon",
  362. "Asia/Yekaterinburg",
  363. "Asia/Yerevan",
  364. "Atlantic/Azores",
  365. "Atlantic/Bermuda",
  366. "Atlantic/Canary",
  367. "Atlantic/Cape_Verde",
  368. "Atlantic/Faroe",
  369. "Atlantic/Jan_Mayen",
  370. "Atlantic/Madeira",
  371. "Atlantic/Reykjavik",
  372. "Atlantic/South_Georgia",
  373. "Atlantic/Stanley",
  374. "Atlantic/St_Helena",
  375. "Australia/Adelaide",
  376. "Australia/Brisbane",
  377. "Australia/Broken_Hill",
  378. "Australia/Canberra",
  379. "Australia/Currie",
  380. "Australia/Darwin",
  381. "Australia/Eucla",
  382. "Australia/Hobart",
  383. "Australia/Lindeman",
  384. "Australia/Lord_Howe",
  385. "Australia/Melbourne",
  386. "Australia/Perth",
  387. "Australia/Sydney",
  388. "Australia/Yancowinna",
  389. "Europe/Amsterdam",
  390. "Europe/Andorra",
  391. "Europe/Astrakhan",
  392. "Europe/Athens",
  393. "Europe/Belfast",
  394. "Europe/Belgrade",
  395. "Europe/Berlin",
  396. "Europe/Bratislava",
  397. "Europe/Brussels",
  398. "Europe/Bucharest",
  399. "Europe/Budapest",
  400. "Europe/Busingen",
  401. "Europe/Chisinau",
  402. "Europe/Copenhagen",
  403. "Europe/Dublin",
  404. "Europe/Gibraltar",
  405. "Europe/Guernsey",
  406. "Europe/Helsinki",
  407. "Europe/Isle_of_Man",
  408. "Europe/Istanbul",
  409. "Europe/Jersey",
  410. "Europe/Kaliningrad",
  411. "Europe/Kirov",
  412. "Europe/Kyiv",
  413. "Europe/Lisbon",
  414. "Europe/Ljubljana",
  415. "Europe/London",
  416. "Europe/Luxembourg",
  417. "Europe/Madrid",
  418. "Europe/Malta",
  419. "Europe/Mariehamn",
  420. "Europe/Minsk",
  421. "Europe/Monaco",
  422. "Europe/Moscow",
  423. "Europe/Nicosia",
  424. "Europe/Oslo",
  425. "Europe/Paris",
  426. "Europe/Podgorica",
  427. "Europe/Prague",
  428. "Europe/Riga",
  429. "Europe/Rome",
  430. "Europe/Samara",
  431. "Europe/San_Marino",
  432. "Europe/Sarajevo",
  433. "Europe/Saratov",
  434. "Europe/Simferopol",
  435. "Europe/Skopje",
  436. "Europe/Sofia",
  437. "Europe/Stockholm",
  438. "Europe/Tallinn",
  439. "Europe/Tirane",
  440. "Europe/Tiraspol",
  441. "Europe/Ulyanovsk",
  442. "Europe/Vaduz",
  443. "Europe/Vatican",
  444. "Europe/Vienna",
  445. "Europe/Vilnius",
  446. "Europe/Volgograd",
  447. "Europe/Warsaw",
  448. "Europe/Zagreb",
  449. "Europe/Zurich",
  450. "GMT",
  451. "Indian/Antananarivo",
  452. "Indian/Chagos",
  453. "Indian/Christmas",
  454. "Indian/Cocos",
  455. "Indian/Comoro",
  456. "Indian/Kerguelen",
  457. "Indian/Mahe",
  458. "Indian/Maldives",
  459. "Indian/Mauritius",
  460. "Indian/Mayotte",
  461. "Indian/Reunion",
  462. "Pacific/Apia",
  463. "Pacific/Auckland",
  464. "Pacific/Bougainville",
  465. "Pacific/Chatham",
  466. "Pacific/Chuuk",
  467. "Pacific/Easter",
  468. "Pacific/Efate",
  469. "Pacific/Fakaofo",
  470. "Pacific/Fiji",
  471. "Pacific/Funafuti",
  472. "Pacific/Galapagos",
  473. "Pacific/Gambier",
  474. "Pacific/Guadalcanal",
  475. "Pacific/Guam",
  476. "Pacific/Honolulu",
  477. "Pacific/Johnston",
  478. "Pacific/Kanton",
  479. "Pacific/Kiritimati",
  480. "Pacific/Kosrae",
  481. "Pacific/Kwajalein",
  482. "Pacific/Majuro",
  483. "Pacific/Marquesas",
  484. "Pacific/Midway",
  485. "Pacific/Nauru",
  486. "Pacific/Niue",
  487. "Pacific/Norfolk",
  488. "Pacific/Noumea",
  489. "Pacific/Pago_Pago",
  490. "Pacific/Palau",
  491. "Pacific/Pitcairn",
  492. "Pacific/Pohnpei",
  493. "Pacific/Port_Moresby",
  494. "Pacific/Rarotonga",
  495. "Pacific/Saipan",
  496. "Pacific/Samoa",
  497. "Pacific/Tahiti",
  498. "Pacific/Tarawa",
  499. "Pacific/Tongatapu",
  500. "Pacific/Wake",
  501. "Pacific/Wallis",
  502. "Pacific/Yap",
  503. "UTC",
  504. }
  505. availableTimezones := make(map[string]string, len(timezones))
  506. for _, tz := range timezones {
  507. availableTimezones[tz] = tz
  508. }
  509. return availableTimezones
  510. }