Skip to content

Instantly share code, notes, and snippets.

@dyno
Created May 31, 2019 05:33
Show Gist options
  • Save dyno/911091bee4314ec21facf60569bfe2ff to your computer and use it in GitHub Desktop.
Save dyno/911091bee4314ec21facf60569bfe2ff to your computer and use it in GitHub Desktop.
date library for jsonnet
// ported date utility function from http://howardhinnant.github.io/date_algorithms.html
// @param z Days since epoch
// @return Civic date tuple [y, m, d]
local civil_from_days(z) = (
local z1 = z + 719468;
local era = std.floor((if z1 >= 0 then z1 else z1 - 146096) / 146097);
local doe = (z1 - era * 146097); // [0, 146096]
local yoe = std.floor((doe - std.floor(doe / 1460) + std.floor(doe / 36524) - std.floor(doe / 146096)) / 365); // [0, 399]
local y = yoe + era * 400;
local doy = doe - (365 * yoe + std.floor(yoe / 4) - std.floor(yoe / 100)); // [0, 365]
local mp = std.floor((5 * doy + 2) / 153); // [0, 11]
local d = doy - std.floor((153 * mp + 2) / 5) + 1; // [1, 31]
local m = mp + (if mp < 10 then 3 else -9); // [1, 12]
[
(if m <= 2 then y + 1 else y),
m,
d,
]
);
// @param t Tuple of [y, m, d]
// @return Days since epoch
local days_from_civil(t) = (
local y = t[0];
local m = t[1];
local d = t[2];
local y1 = if m <= 2 then y - 1 else y;
local era = std.floor((if y1 >= 0 then y1 else y1 - 399) / 400);
local yoe = (y1 - era * 400); // [0, 399]
local doy = std.floor((153 * (m + (if m > 2 then -3 else 9)) + 2) / 5) + d - 1; // [0, 365]
local doe = yoe * 365 + std.floor(yoe / 4) - std.floor(yoe / 100) + doy; // [0, 146096]
era * 146097 + std.floor(doe) - 719468
);
local format_iso(t) = '%4d-%02d-%02d' % t;
local parse_iso(s) = std.map(std.parseInt, std.split(s, '-'));
local format_compact(t) = '%4d%02d%02d' % t;
local parse_compact(s) = std.map(std.parseInt, [s[:4], s[4:6], s[6:8]]);
{
civil_from_days:: civil_from_days,
days_from_civil:: days_from_civil,
format_iso:: format_iso,
parse_iso:: parse_iso,
format_compact:: format_compact,
parse_compact:: parse_compact,
}
@dyno
Copy link
Author

dyno commented May 31, 2019

local lib = import 'date.libsonnet';

{
  test_days_from_civil_epoch: std.assertEqual(lib.days_from_civil([1970, 1, 1]), 0),
  test_days_from_civil: std.assertEqual(lib.days_from_civil([2019, 5, 30]), 18046),
  test_civil_from_days_epoch: std.assertEqual(lib.civil_from_days(0), [1970, 1, 1]),
  test_civil_from_days: std.assertEqual(lib.civil_from_days(18046), [2019, 5, 30]),
  test_format_iso: std.assertEqual(lib.format_iso([2019, 5, 30]), '2019-05-30'),
  test_parse_iso: std.assertEqual(lib.parse_iso('2019-05-30'), [2019, 5, 30]),
  test_format_compact: std.assertEqual(lib.format_compact([2019, 5, 30]), '20190530'),
  test_parse_compact: std.assertEqual(lib.parse_compact('20190530'), [2019, 5, 30]),
}

@max-voloshin
Copy link

max-voloshin commented Jun 16, 2020

Object-oriented edition:

// ported from: https://gist.github.com/dyno/911091bee4314ec21facf60569bfe2ff

// @param days since epoch
// @return [y, m, d]
local days_to_civil(days) = (
  local z1 = days + 719468;
  local era = std.floor((if z1 >= 0 then z1 else z1 - 146096) / 146097);
  local doe = (z1 - era * 146097);  // [0, 146096]
  local yoe = std.floor((doe - std.floor(doe / 1460) + std.floor(doe / 36524) - std.floor(doe / 146096)) / 365);  // [0, 399]
  local y = yoe + era * 400;
  local doy = doe - (365 * yoe + std.floor(yoe / 4) - std.floor(yoe / 100));  // [0, 365]
  local mp = std.floor((5 * doy + 2) / 153);  // [0, 11]
  local d = doy - std.floor((153 * mp + 2) / 5) + 1;  // [1, 31]
  local m = mp + (if mp < 10 then 3 else -9);  // [1, 12]
  [if m <= 2 then y + 1 else y, m, d]
);

// @param civil [y, m, d]
// @return date object
local from_civil(civil) = {
    iso(): '%4d-%02d-%02d' % civil,
    compact(): '%4d%02d%02d' % civil,
    days(): (
        local y = civil[0];
        local m = civil[1];
        local d = civil[2];
        local y1 = if m <= 2 then y - 1 else y;
        local era = std.floor((if y1 >= 0 then y1 else y1 - 399) / 400);
        local yoe = (y1 - era * 400);  // [0, 399]
        local doy = std.floor((153 * (m + (if m > 2 then -3 else 9)) + 2) / 5) + d - 1;  // [0, 365]
        local doe = yoe * 365 + std.floor(yoe / 4) - std.floor(yoe / 100) + doy;  // [0, 146096]
        era * 146097 + std.floor(doe) - 719468
    ),
    add(offset): (
        from_civil(days_to_civil(self.days() + offset))
    ),
    sub(offset): (
        self.add(-offset)
    )
};

// @param days since epoch
// @return date object
local from_days(days) = (
  from_civil(days_to_civil(days))
);

// @param s %4d-%02d-%02d
// @return date object
local from_iso(s) = from_civil(std.map(std.parseInt, std.split(s, '-')));

// @param s %4d%02d%02d
// @return date object
local from_compact(s) = from_civil(std.map(std.parseInt, [s[:4], s[4:6], s[6:8]]));

// @param timestamp since epoch
// @return date object
local from_timestamp(timestamp) = from_days(std.floor(timestamp / 60 / 60 / 24));

{
    from_civil:: from_civil,
    from_days:: from_days,
    from_iso:: from_iso,
    from_compact:: from_compact,
    from_timestamp:: from_timestamp,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment