Skip to content

Instantly share code, notes, and snippets.

@hatzka-nezumi
Last active December 4, 2015 19:00
Show Gist options
  • Save hatzka-nezumi/295bb86c659151166d0c to your computer and use it in GitHub Desktop.
Save hatzka-nezumi/295bb86c659151166d0c to your computer and use it in GitHub Desktop.
Levian calendar calculator. https://www.vikomprenas.com/levcal/
// Levian Calendar calculator
// Copyright (C) 2015 Vi Komprenas <[email protected]>
// This is free and unencumbered software released into the public domain. <http://unlicense.org/>
// Requires jQuery (tested on 1.11.3).
// <div id="levcal">Loading!</div>
jQuery(function levcal(){
var $ = window.jQuery;
var widgetcontent = "";
widgetcontent += '<form>';
widgetcontent += '<input type="button" value="Clear input" id="levcal-reset"></input>';
widgetcontent += 'Gregorian calendar date (YYYY-MM-DD): <input type="text" autocomplete="off" id="levcal-gregorian"></input><br/>';
widgetcontent += '<input type="button" value="Help" id="levcal-help"></input>';
widgetcontent += '<a href="https://www.nationstates.net/nation=leveat/detail=factbook/id=484140">Levian calendar</a> date (Y/M/W.D or Y//X.D): <input type="text" autocomplete="off" id="levcal-levian"></input>';
widgetcontent += '<div id="levcal-message"></div>';
widgetcontent += '</form>';
function message(mesg) {
$("#levcal-message").html(mesg + '<br/>');
}
function defaultmessage() {
message("Levian calendar calculator. <a href=\"http://www.vikomprenas.com/levcal/\">Made by Vi Komprenas</a>.")
}
function daysInMonth(month, isLeapYear) {
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
return 31;
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30;
} else if (month == 2) { // feb
return isLeapYear ? 29 : 28;
} else { // emergency
return -1;
}
}
function isLeapYear(year) {
var leap = (year % 4 == 0);
if (year % 100 == 0) {
leap = leap && (year % 400 == 0);
}
return leap;
}
// https://stackoverflow.com/questions/10073699/pad-a-number-with-leading-zeros-in-javascript
function pad(n, width, z) {
z = z || '0';
n = n + '';
return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n;
}
function updateLevian() {
// First, verify the Gregorian date at least looks like a date
var gregstr = $("#levcal-gregorian").val();
if (gregstr == "") {
defaultmessage(); // don't bother the user if the box is blank
return;
} else if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(gregstr)) {
message("Invalid Gregorian date.");
return;
} else {
defaultmessage();
}
// Now split it apart and verify it in more detail
var gregdate = gregstr.split("-");
var gregyear = parseInt(gregdate[0]);
var gregmonth = parseInt(gregdate[1]);
var gregday = parseInt(gregdate[2]);
if (gregyear == 0 || // we won't try to handle dates BCE yet
gregmonth == 0 ||
gregmonth > 12 ||
gregday == 0) {
message("Invalid Gregorian date.");
return;
}
// Last verification step: make sure gregday is within bounds for gregmonth
var daysingregmonth = daysInMonth(gregmonth, isLeapYear(gregyear));
if (daysingregmonth == -1) {
message("Error: couldn't compute number of days in that month.");
return;
}
if (gregday > daysingregmonth) {
message("Invalid Gregorian date.");
return;
}
// Now actually compute the Levian date
var levyear;
var levmonth;
var levweek;
var levday;
// First, compute number of days since 1800-01-01 (epoch)
if (gregyear < 1800) {
message("Support for dates prior to 1800 is not yet available.");
return;
}
var daysSinceEpoch = 0;
for (y = 1800; y < gregyear; y++) { // don't include the year of the input date yet
daysSinceEpoch += (isLeapYear(y)) ? 366 : 365;
}
for (m = 1; m < gregmonth; m++) {
daysSinceEpoch += daysInMonth(m, isLeapYear(gregyear));
}
daysSinceEpoch += gregday - 1; // -1 since there is no day 0 in any G. (or L.) month
var daysLeft = daysSinceEpoch; // renaming to better illustrate its role in the next bit
console.log(daysLeft);
// Remove days to make a year
levyear = Math.floor(daysLeft / 365);
daysLeft -= 365 * levyear;
levyear += 1;
// Remove days to make a month
levmonth = Math.floor(daysLeft / 30);
daysLeft -= 30 * levmonth;
levmonth += 1;
// Work out what week of the month it is
levweek = Math.floor(daysLeft / 5);
daysLeft -= 5 * levweek;
levweek += 1;
// And the day.
levday = daysLeft
daysLeft -= levday;
levday += 1;
// Now make it into a string
var levstr = levyear + '/' + levmonth + '/' + levweek + '.' + levday;
if (levmonth == 13) {
levstr = levyear + '//X.' + levday;
}
$("#levcal-levian").val(levstr);
}
function updateGregorian() {
// First, verify the Levian date at least looks like a date
var levstr = $("#levcal-levian").val();
if (levstr == "") {
defaultmessage(); // Don't bother the user if it's blank
return;
} else if (!/^[0-9]+\/([0-9]+\/[1-6]|\/X)\.[1-5]$/.test(levstr)) {
message("Invalid Levian date.");
return;
} else {
defaultmessage();
}
// Now split it apart and verify it in more detail
var levdate = levstr.split('/');
var levyear = parseInt(levdate[0]);
var levmonth;
var levweek;
var levday = parseInt(levdate[2].split('.')[1]);
var levfree = /^[0-9]+\/\/X\.[1-5]$/.test(levstr);
if (levfree) {
levmonth = 13;
levweek = 1;
} else {
levmonth = parseInt(levdate[1]);
levweek = parseInt(levdate[2].split('.')[0]);
if (levmonth == 13) {
// Not so fast.
message("Invalid Levian date.");
return;
}
}
if (levmonth < 1 || levmonth > 13 ||
levweek < 1 || levweek > 6 ||
levday < 1 || levday > 5 ||
levmonth == 13 && levweek != 1) {
message("Invalid Levian date.");
return;
}
// Now actually compute the Gregorian date
// First, convert the Levian date to days since the epoch
var daysSinceEpoch = levday - 1;
daysSinceEpoch += (levweek - 1) * 5;
daysSinceEpoch += (levmonth - 1) * 30;
daysSinceEpoch += (levyear - 1) * 365;
// Then, start adding those days into a Gregorian date.
var gregyear = 1800;
var gregmonth = 1;
var gregday = 1;
// I could divide, too, but this is neater to understand
for (daysLeft=daysSinceEpoch; daysLeft > 0; daysLeft--) {
gregday += 1;
if (gregday > daysInMonth(gregmonth, isLeapYear(gregyear))) {
gregday = 1;
gregmonth += 1;
if (gregmonth > 12) {
gregmonth = 1;
gregyear += 1;
}
}
}
// Lastly, stringify and display the result
var gregstr = pad(gregyear, 4) + "-" + pad(gregmonth, 2) + "-" + pad(gregday, 2);
$("#levcal-gregorian").val(gregstr);
}
function reset() {
defaultmessage();
$("#levcal-gregorian").val("");
$("#levcal-levian").val("");
}
function help() {
message("Enter a date in either box and press Enter for magic.");
}
function init() {
$("#levcal").html(widgetcontent);
$("#levcal").css("font-weight", "400");
$("#levcal-message").css("font-weight", "600");
$("#levcal-reset").css("float", "right");
$("#levcal-reset").click(reset);
$("#levcal-help").css("float", "right");
$("#levcal-help").click(help);
$("#levcal-gregorian").change(updateLevian);
$("#levcal-levian").change(updateGregorian);
defaultmessage();
}
init();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment