Skip to content

Commit

Permalink
revise Timezone.deinit (#37)
Browse files Browse the repository at this point in the history
* prepare next version

* Remove constCast in Timezone.deinit (#36)

* ensure UTC can be de-inited safely

* update code comments / docs

* rename runtimeFromTzfile to fromSystemTzdata

* update readme and changelog

* update changelog

* update test

---------

Co-authored-by: Ratakor <[email protected]>
  • Loading branch information
FObersteiner and Ratakor authored Oct 28, 2024
1 parent b5d3204 commit ca1811a
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 72 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,22 @@ Types of changes

## Unreleased

## 2024-10-28, v0.4.1

### Added

- test UTC Timezone can be deinitialized safely
- test UTC offset of a datetime stays untouched if its Timezone gets deinitialized

### Changed

- make Timezone.deinit take a pointer to a Timezone instead of a *const (Timezones must be declared as 'var') - by @Ratakor
- (breaking) renamed: 'Timezone.runtimeFromTzfile' to 'Timezone.fromSystemTzdata'

### Fixed

- Timezone.UTC can be deinitialized safely now (see Timezone.deinit change) - by @Ratakor

## 2024-10-27, v0.4.0

### Added
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ defer _ = gpa.deinit();
const allocator = gpa.allocator();
// zdt embeds the IANA tz database:
const tz_LA = try zdt.Timezone.fromTzdata("America/Los_Angeles", allocator);
var tz_LA = try zdt.Timezone.fromTzdata("America/Los_Angeles", allocator);
defer tz_LA.deinit();
// you can also use your system's tz data at runtime;
// this will very likely not work on Windows, so we use the embedded version here as well.
const tz_Paris = switch (builtin.os.tag) {
var tz_Paris = switch (builtin.os.tag) {
.windows => try zdt.Timezone.fromTzdata("Europe/Paris", allocator),
else => try zdt.Timezone.runtimeFromTzfile("Europe/Paris", zdt.Timezone.tzdb_prefix, allocator),
else => try zdt.Timezone.fromSystemTzdata("Europe/Paris", zdt.Timezone.tzdb_prefix, allocator),
};
defer tz_Paris.deinit();
Expand Down
2 changes: 1 addition & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const std = @import("std");
const builtin = @import("builtin");
const log = std.log.scoped(.zdt_build);

const zdt_version = std.SemanticVersion{ .major = 0, .minor = 4, .patch = 0 };
const zdt_version = std.SemanticVersion{ .major = 0, .minor = 4, .patch = 1 };

const example_files = [_][]const u8{
"ex_demo",
Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.{
.name = "zdt",
.version = "0.4.0",
.version = "0.4.1",
.paths = .{
"zdt.zig",
"lib", // anything from lib
Expand Down
6 changes: 3 additions & 3 deletions examples/ex_demo.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ pub fn main() !void {
const allocator = gpa.allocator();

// zdt embeds the IANA tz database:
const tz_LA = try zdt.Timezone.fromTzdata("America/Los_Angeles", allocator);
var tz_LA = try zdt.Timezone.fromTzdata("America/Los_Angeles", allocator);
defer tz_LA.deinit();

// you can also use your system's tz data at runtime;
// this will very likely not work on Windows, so we use the embedded version here as well.
const tz_Paris = switch (builtin.os.tag) {
var tz_Paris = switch (builtin.os.tag) {
.windows => try zdt.Timezone.fromTzdata("Europe/Paris", allocator),
else => try zdt.Timezone.runtimeFromTzfile("Europe/Paris", zdt.Timezone.tzdb_prefix, allocator),
else => try zdt.Timezone.fromSystemTzdata("Europe/Paris", zdt.Timezone.tzdb_prefix, allocator),
};
defer tz_Paris.deinit();

Expand Down
6 changes: 3 additions & 3 deletions examples/ex_timezones.zig
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@ pub fn main() !void {
println("time zone database version: {s}", .{Tz.tzdb_version});
println("path to local tz database: {s}\n", .{Tz.tzdb_prefix});

const tz_berlin: Tz = try Tz.fromTzdata("Europe/Berlin", allocator);
var tz_berlin: Tz = try Tz.fromTzdata("Europe/Berlin", allocator);
defer tz_berlin.deinit();
var now_berlin: Datetime = try Datetime.now(.{ .tz = &tz_berlin });
const now_utc: Datetime = Datetime.nowUTC();
println("Now, UTC time : {s}", .{now_utc});
println("Now, Berlin time : {s} ({s})", .{ now_berlin, now_berlin.tzAbbreviation() });
println("Datetimes have UTC offset / time zone? : {}, {}\n", .{ now_utc.isAware(), now_berlin.isAware() });

const my_tz: Tz = try Tz.tzLocal(allocator);
var my_tz: Tz = try Tz.tzLocal(allocator);
defer my_tz.deinit();
var now_local = try now_berlin.tzConvert(.{ .tz = &my_tz });
println("My time zone : {s}", .{my_tz.name()});

println("Now, my time zone : {s} ({s})", .{ now_local, now_local.tzAbbreviation() });
println("", .{});

const tz_ny = try Tz.fromTzdata("America/New_York", allocator);
var tz_ny = try Tz.fromTzdata("America/New_York", allocator);
defer tz_ny.deinit();
var now_ny: Datetime = try now_local.tzConvert(.{ .tz = &tz_ny });
println("Now in New York : {s} ({s})", .{ now_ny, now_ny.tzAbbreviation() });
Expand Down
7 changes: 3 additions & 4 deletions lib/Datetime.zig
Original file line number Diff line number Diff line change
Expand Up @@ -449,12 +449,11 @@ pub fn fromUnix(
fn __normalize(dt: *Datetime) TzError!void {
// "local" Unix time to get the fields right:
var fake_unix = dt.unix_sec;
// if a time zone is defined, this takes precedence.
// TODO : tz might be UTC
// if a time zone is defined, this takes precedence and overwrites the UTC offset if one is specified.
if (dt.tz) |tz_ptr| {
dt.utc_offset = try UTCoffset.atUnixtime(tz_ptr, dt.unix_sec);
fake_unix += dt.utc_offset.?.seconds_east;
} else if (dt.utc_offset) |off| {
} else if (dt.utc_offset) |off| { // having only a UTC offset (no tz) is also fine.
fake_unix += off.seconds_east;
}

Expand Down Expand Up @@ -867,7 +866,7 @@ pub fn getSurroundingTimetypes(idx: i32, _tz: *const Timezone) ![3]?*tzif.Timety
}
return surrounding;
},
.utc => return TzError.NotImplemented,
.posixtz => return TzError.NotImplemented,
.utc => return TzError.NotImplemented,
}
}
24 changes: 12 additions & 12 deletions lib/Timezone.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ const ruleTypes = enum {
utc,
};

/// rules for a time zone
/// rule sources for a time zone
rules: union(ruleTypes) {
/// IANA tz-db/tzdata TZif file
/// IANA tz-db/tzdata TZif file;
/// use Timezone.fromTzdata or Timezone.fromSystemTzdata to set as time zone of a datetime.
tzif: tzif.Tz,
/// POSIX TZ string
/// Not implemented! - POSIX TZ string
posixtz: posix.Tz,
// UTC placeholder; constant offset of zero
/// UTC placeholder;
/// use Timezone.UTC constant to set UTC as time zone of a datetime.
utc: struct {},
},

Expand Down Expand Up @@ -89,7 +91,7 @@ pub fn fromTzdata(identifier: []const u8, allocator: std.mem.Allocator) TzError!
/// To use the system's tzdata, use 'zdt.Timezone.tzdb_prefix'.
/// The caller must make sure to de-allocate memory used for storing the TZif file's content
/// by calling the deinit method of the returned Timezone instance.
pub fn runtimeFromTzfile(identifier: []const u8, db_path: []const u8, allocator: std.mem.Allocator) TzError!Timezone {
pub fn fromSystemTzdata(identifier: []const u8, db_path: []const u8, allocator: std.mem.Allocator) TzError!Timezone {
if (!identifierValid(identifier)) return TzError.InvalidIdentifier;
var path_buffer: [std.fs.max_path_bytes]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&path_buffer);
Expand Down Expand Up @@ -133,14 +135,12 @@ pub fn runtimeFromTzfile(identifier: []const u8, db_path: []const u8, allocator:
}

/// Clear a TZ instance and free potentially used memory
pub fn deinit(tz: *const Timezone) void {
// must remove const qualifier to clear present time zone rules
const _tz_ptr = @constCast(tz);
_tz_ptr.__name_data = std.mem.zeroes([cap_name_data]u8);
_tz_ptr.__name_data_len = 0;
pub fn deinit(tz: *Timezone) void {
tz.__name_data = std.mem.zeroes([cap_name_data]u8);
tz.__name_data_len = 0;

switch (tz.rules) {
.tzif => _tz_ptr.rules.tzif.deinit(),
.tzif => |*_tzif| _tzif.deinit(),
.posixtz => return,
.utc => return,
}
Expand All @@ -158,7 +158,7 @@ pub fn tzLocal(allocator: std.mem.Allocator) TzError!Timezone {
var path_buffer: [std.fs.max_path_bytes]u8 = undefined;
const path = std.fs.realpath(default_path, &path_buffer) catch
return TzError.TZifUnreadable;
return try Timezone.runtimeFromTzfile(path, "", allocator);
return try Timezone.fromSystemTzdata(path, "", allocator);
},
.windows => {
const win_name = tzwin.getTzName() catch
Expand Down
Loading

0 comments on commit ca1811a

Please sign in to comment.