Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lightweight timestamp-to-ISO conversion function #2034

Closed
sffc opened this issue Jun 9, 2022 · 6 comments
Closed

Add lightweight timestamp-to-ISO conversion function #2034

sffc opened this issue Jun 9, 2022 · 6 comments
Assignees
Labels
C-datetime Component: datetime, calendars, time zones good first issue Good for newcomers S-small Size: One afternoon (small bug fix or enhancement) T-core Type: Required functionality

Comments

@sffc
Copy link
Member

sffc commented Jun 9, 2022

For time zone calculations, we need to store local datetimes (also known as civil datetimes or plain datetimes) for metazone cutoffs in the data provider.

If we store them as an ISO string, like "1988-04-03 04:01", we need either 12 or 16 bytes. It would be more efficient to store them as a "local timestamp", or the duration between the local datetime and 1970-01-01T00:00, most likely measured in minutes and stored in a u32. (Note: we should be very careful on using the term "local timestamp", because that can be really confusing terminology.)

I think this conversion function could go in icu_calendar.

@sffc sffc added T-core Type: Required functionality C-datetime Component: datetime, calendars, time zones S-small Size: One afternoon (small bug fix or enhancement) good first issue Good for newcomers labels Jun 9, 2022
@sffc
Copy link
Member Author

sffc commented Jun 9, 2022

CC @Manishearth

@sffc sffc added the help wanted Issue needs an assignee label Jun 9, 2022
@sffc sffc added this to the ICU4X 1.0 (Features) milestone Jun 9, 2022
@sffc sffc removed the help wanted Issue needs an assignee label Jun 9, 2022
@sffc
Copy link
Member Author

sffc commented Jun 9, 2022

@samchen61661: Manish will post a suggestion on how to easily implement this function in icu_calendar.

Note that we likely want this to work bi-directionally. ISO-to-timestamp is probably what we need first.

@Manishearth
Copy link
Member

We don't have this but it ought to be easy to implement, we already have internal "from_fixed" and "to_fixed" functions which convert to and from "fixed" dates, counted as days since 1 AD. So adding this function to DateTime<Iso> would involve pulling out the h/m/s, then taking a difference between the fixed date and the fixed date for the unix epoch, and then multiplying and adding.

// Fixed is day count representation of calendars starting from Jan 1st of year 1.
// The fixed calculations algorithms are from the Calendrical Calculations book.
//
// Lisp code reference: https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1167-L1189
pub(crate) fn fixed_from_iso(date: IsoDateInner) -> i32 {
// Calculate days per year
let mut fixed: i32 = EPOCH - 1 + 365 * (date.year.0 - 1);
// Adjust for leap year logic
fixed += ((date.year.0 - 1) / 4) - ((date.year.0 - 1) / 100) + ((date.year.0 - 1) / 400);
// Days of current year
fixed += (367 * (date.month.0 as i32) - 362) / 12;
// Leap year adjustment for the current year
fixed += if date.month.0 <= 2 {
0
} else if Self::is_leap_year(date.year) {
-1
} else {
-2
};
// Days passed in current month
fixed + (date.day.0 as i32)
}
fn fixed_from_iso_integers(year: i32, month: i32, day: i32) -> i32 {
#[allow(clippy::unwrap_used)] // TODO(#1668) Clippy exceptions need docs or fixing.
Self::fixed_from_iso(
*Date::new_iso_date_from_integers(year, month as u8, day as u8)
.unwrap()
.inner(),
)
}
// Lisp code reference: https://github.com/EdReingold/calendar-code2/blob/1ee51ecfaae6f856b0d7de3e36e9042100b4f424/calendar.l#L1191-L1217
fn iso_year_from_fixed(date: i32) -> i32 {
// 400 year cycles have 146097 days
let n_400 = date / 146097;
let date = date % 146097;
// 100 year cycles have 36524 days
let n_100 = date / 36524;
let date = date % 36524;
// 4 year cycles have 1461 days
let n_4 = date / 1461;
let date = date % 1461;
let n_1 = date / 365;
let year = 400 * n_400 + 100 * n_100 + 4 * n_4 + n_1;
if n_100 == 4 || n_1 == 4 {
year
} else {
year + 1
}
}

@sffc
Copy link
Member Author

sffc commented Jun 14, 2022

Note: I think the desired output for the string-to-integer function is a signed number of minutes since Januay 1, 1970 in local time. Probably an i32.

@sffc
Copy link
Member Author

sffc commented Jul 28, 2022

@samchen61661 Is this complete?

@samchen61661
Copy link
Member

@samchen61661 Is this complete?

Yeah, marked it as closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-datetime Component: datetime, calendars, time zones good first issue Good for newcomers S-small Size: One afternoon (small bug fix or enhancement) T-core Type: Required functionality
Projects
None yet
Development

No branches or pull requests

3 participants