Skip to content

Commit

Permalink
TzDateTime enhancements c3lang#1473
Browse files Browse the repository at this point in the history
  • Loading branch information
hkalexling committed Sep 26, 2024
1 parent bf9ae2f commit 8ce30bf
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 2 deletions.
85 changes: 84 additions & 1 deletion lib/std/time/datetime.c3
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ fn DateTime from_date(int year, Month month = JANUARY, int day = 1, int hour = 0
return dt;
}

/**
* @require day >= 1 && day < 32
* @require hour >= 0 && hour < 24
* @require min >= 0 && min < 60
* @require sec >= 0 && sec < 60
* @require us >= 0 && us < 999_999
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime from_date_tz(int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0, int gmt_offset = 0)
{
return from_date(year, month, day, hour, min, sec, us).with_gmt_offset(gmt_offset);
}

fn TzDateTime DateTime.to_local(&self)
{
Tm tm @noinit;
Expand Down Expand Up @@ -47,6 +60,57 @@ fn TzDateTime DateTime.to_local(&self)
return dt;
}

/**
* Update timestamp to gmt_offset while keeping the date and time
* values unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime DateTime.with_gmt_offset(self, int gmt_offset)
{
TzDateTime dt = { self, 0 };
return dt.with_gmt_offset(gmt_offset);
}

/**
* Update timestamp to gmt_offset while keeping the date and time
* values unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime TzDateTime.with_gmt_offset(self, int gmt_offset)
{
self.time -= (Time)(gmt_offset - self.gmt_offset) * (Time)time::SEC;
return { self.date_time, gmt_offset };
}

/**
* Update the date and time values to gmt_offset while keeping the
* timestamp unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure self.time == return.time
**/
fn TzDateTime DateTime.to_gmt_offset(self, int gmt_offset)
{
TzDateTime dt = { self, 0 };
return dt.to_gmt_offset(gmt_offset);
}

/**
* Update the date and time values to gmt_offset while keeping the
* timestamp unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure self.time == return.time
**/
fn TzDateTime TzDateTime.to_gmt_offset(self, int gmt_offset) {
Time time = self.time + (Time)(gmt_offset - self.gmt_offset) * (Time)time::SEC;
DateTime dt = from_time(time);
dt.time = self.time;
return { dt, gmt_offset };
}

/**
* @require day >= 1 && day < 32
* @require hour >= 0 && hour < 24
Expand Down Expand Up @@ -121,13 +185,32 @@ fn DateTime DateTime.add_months(&self, int months)
return from_date(year, (Month)month, self.day, self.hour, self.min, self.sec, self.usec);
}


fn TzDateTime TzDateTime.add_seconds(&self, int seconds) => self.date_time.add_seconds(seconds).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_minutes(&self, int minutes) => self.date_time.add_minutes(minutes).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_hours(&self, int hours) => self.date_time.add_hours(hours).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_days(&self, int days) => self.date_time.add_days(days).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_weeks(&self, int weeks) => self.date_time.add_weeks(weeks).to_gmt_offset(self.gmt_offset);

fn TzDateTime TzDateTime.add_years(&self, int years) => self.date_time.add_years(years).with_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_months(&self, int months) => self.date_time.add_months(months).with_gmt_offset(self.gmt_offset);

fn DateTime from_time(Time time)
{
DateTime dt @noinit;
dt.set_time(time) @inline;
return dt;
}

/**
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure time == return.time
**/
fn TzDateTime from_time_tz(Time time, int gmt_offset)
{
return from_time(time).to_gmt_offset(gmt_offset);
}

fn Time DateTime.to_time(&self) @inline
{
return self.time;
Expand Down Expand Up @@ -167,4 +250,4 @@ fn double DateTime.diff_sec(self, DateTime from)
fn Duration DateTime.diff_us(self, DateTime from)
{
return self.time.diff_us(from.time);
}
}
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
- Add `io::read_new_fully` for reading to the end of a stream.
- Add `io::wrap_bytes` for reading bytes with `io` functions.
- Add `rnd` and `rand_in_range` default random functions.
- Additional timezone related functions for `datetime`.

## 0.6.2 Change list

Expand Down
87 changes: 86 additions & 1 deletion test/unit/stdlib/time/datetime.c3
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,89 @@ DateTime d = datetime::from_date(1973, APRIL, 27);
x = d.add_months(0);
assert(x.year == 1975);
assert(x.month == MAY);
}
}

fn void test_timezone()
{
int offset_hours = 7;
int offset = offset_hours * 3600;

DateTime d1 = datetime::from_date(2022, OCTOBER, 15);
TzDateTime d2 = datetime::from_date_tz(2022, OCTOBER, 15, gmt_offset: offset);

DateTime dt;
TzDateTime tz;

Time t1 = d1.time;
Time t2 = d2.time;

assert(t1 == 1665792000000000);
assert(t2 == 1665766800000000);

// to_gmt_offset should keep the timesampt value
tz = d1.to_gmt_offset(offset);
assert(tz.time == t1);
assert(tz.year == 2022);
assert(tz.month == OCTOBER);
assert(tz.day == 15);
assert(tz.hour == offset_hours);
assert(tz.min == 0);
assert(tz.gmt_offset == offset);

// with_gmt_offset should keep the date/time values and adjust the timestamp
tz = d1.with_gmt_offset(offset);
assert(tz.time == t2);
assert(tz.year == d1.year);
assert(tz.month == d1.month);
assert(tz.day == d1.day);
assert(tz.hour == d1.hour);
assert(tz.min == d1.min);
assert(tz.gmt_offset == offset);

dt = datetime::from_time(t1);
assert(dt.day == 15);
assert(dt.hour == 0);
dt = datetime::from_time(t2);
assert(dt.day == 14);
assert(dt.hour == 24 - offset_hours);

// from_time_tz should keep the timesampt value
tz = datetime::from_time_tz(t1, offset);
assert(tz.time == t1);
assert(tz.day == 15);
assert(tz.hour == offset_hours);
assert(tz.gmt_offset == offset);
tz = datetime::from_time_tz(t2, offset);
assert(tz.time == t2);
assert(tz.day == 15);
assert(tz.hour == 0);
assert(tz.gmt_offset == offset);

// The add_* methods should adjust the targeted date/time values while
// keeping the others unchanged. The gmt_offset should be kept as well.
dt = d1.add_hours(1);
assert(dt.day == 15);
assert(dt.hour == 1);
tz = d2.add_hours(1);
assert(tz.day == 15);
assert(tz.hour == 1);
assert(tz.gmt_offset == offset);

dt = d1.add_days(1);
assert(dt.day == 16);
assert(dt.hour == 0);
tz = d2.add_days(1);
assert(tz.day == 16);
assert(tz.hour == 0);
assert(tz.gmt_offset == offset);

dt = d1.add_months(1);
assert(dt.month == NOVEMBER);
assert(dt.day == 15);
assert(dt.hour == 0);
tz = d2.add_months(1);
assert(tz.month == NOVEMBER);
assert(tz.day == 15);
assert(tz.hour == 0);
assert(tz.gmt_offset == offset);
}

0 comments on commit 8ce30bf

Please sign in to comment.