tz_unix.odin 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. #+build darwin, linux, freebsd, openbsd, netbsd
  2. #+private
  3. package timezone
  4. import "core:os"
  5. import "core:strings"
  6. import "core:path/filepath"
  7. import "core:time/datetime"
  8. local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) {
  9. local_str, ok := os.lookup_env("TZ", allocator)
  10. if !ok {
  11. orig_localtime_path := "/etc/localtime"
  12. path, err := os.absolute_path_from_relative(orig_localtime_path, allocator)
  13. if err != nil {
  14. // If we can't find /etc/localtime, fallback to UTC
  15. if err == .ENOENT {
  16. str, err2 := strings.clone("UTC", allocator)
  17. if err2 != nil { return }
  18. return str, true
  19. }
  20. return
  21. }
  22. defer delete(path, allocator)
  23. // FreeBSD makes me sad.
  24. // This is a hackaround, because FreeBSD copies rather than softlinks their local timezone file,
  25. // *sometimes* and then stores the original name of the timezone in /var/db/zoneinfo instead
  26. if path == orig_localtime_path {
  27. data := os.read_entire_file("/var/db/zoneinfo", allocator) or_return
  28. return strings.trim_right_space(string(data)), true
  29. }
  30. // Looking for tz path (ex fmt: "UTC", "Etc/UTC" or "America/Los_Angeles")
  31. path_dir, path_file := filepath.split(path)
  32. if path_dir == "" {
  33. return
  34. }
  35. upper_path_dir, upper_path_chunk := filepath.split(path_dir[:len(path_dir)-1])
  36. if upper_path_dir == "" {
  37. return
  38. }
  39. if strings.contains(upper_path_chunk, "zoneinfo") {
  40. region_str, err := strings.clone(path_file, allocator)
  41. if err != nil { return }
  42. return region_str, true
  43. } else {
  44. region_str, err := filepath.join({upper_path_chunk, path_file}, allocator = allocator)
  45. if err != nil { return }
  46. return region_str, true
  47. }
  48. }
  49. if local_str == "" {
  50. delete(local_str, allocator)
  51. str, err := strings.clone("UTC", allocator)
  52. if err != nil { return }
  53. return str, true
  54. }
  55. return local_str, true
  56. }
  57. _region_load :: proc(_reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) {
  58. reg_str := _reg_str
  59. if reg_str == "UTC" {
  60. return nil, true
  61. }
  62. if reg_str == "local" {
  63. local_name := local_tz_name(allocator) or_return
  64. if local_name == "UTC" {
  65. delete(local_name, allocator)
  66. return nil, true
  67. }
  68. reg_str = local_name
  69. }
  70. defer if _reg_str == "local" { delete(reg_str, allocator) }
  71. db_path := "/usr/share/zoneinfo"
  72. region_path := filepath.join({db_path, reg_str}, allocator)
  73. defer delete(region_path, allocator)
  74. return load_tzif_file(region_path, reg_str, allocator)
  75. }