Skip to content

Commit

Permalink
Adds more days in month data
Browse files Browse the repository at this point in the history
This means we can convert a wider range of dates.

#8
  • Loading branch information
garethbowen authored Jan 23, 2019
1 parent 18cd8a2 commit 77c090a
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 42 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,19 @@ twitter-bootstrap widget [available from `npm`](https://www.npmjs.com/package/bi
require('bikram-sambat-bootstrap');

For usage example, see `bootstrap/dist`.

# Development

## Run tests

1. Install android sdk
2. Execute `make test`

## Update compressed days in month data

1. Update `/test-data/daysInMonth.json` as required
2. The first entry in `/test-data/daysInMonth.json` as the BS Epoch. Take this
and convert it to AD and update the bsEpoch constant below.
3. Run the script: node ./scripts/encode-days-in-month.js
4. Copy the output code into the files overwriting the existing hardcoded values

42 changes: 20 additions & 22 deletions java/lib/src/main/java/bikramsambat/BsCalendar.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,17 @@

public final class BsCalendar {

// We have defined our own Epoch for Bikram Sambat:
// 1-1-2007 BS / 13-4-1950 AD
private static final long MS_PER_DAY = 86400000L;
private static final long BS_EPOCH_TS = -622359900000L; // 1950-4-13 AD
private static final long BS_YEAR_ZERO = 2007L;
// ------ TO UPDATE THESE HARDCODED VALUES USE /scripts/encode-days-in-month.js
// We have defined our own Epoch for Bikram Sambat: 1970-1-1 BS or 1913-4-13 AD
private static final long BS_EPOCH_TS = -1789990200000L; // 1913-4-13 AD
private static final int BS_YEAR_ZERO = 1970;
private static final long[] ENCODED_MONTH_LENGTHS = {
5315258L,5314490L,9459438L,8673005L,5315258L,5315066L,9459438L,8673005L,5315258L,5314298L,9459438L,5327594L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,9459438L,5315306L,5315258L,5314286L,8673006L,5315306L,5315258L,5265134L,8673006L,5315258L,5315258L,9459438L,8673005L,5315258L,5314298L,9459438L,8673005L,5315258L,5314298L,9459438L,8473322L,5315258L,5314298L,9459438L,5327594L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,8673006L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,9459438L,8673005L,5315258L,5314490L,9459438L,8673005L,5315258L,5314298L,9459438L,8473325L,5315258L,5314298L,9459438L,5327594L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,9459438L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,5265134L,8673006L,5315258L,5314490L,9459438L,8673005L,5315258L,5314298L,9459438L,8669933L,5315258L,5314298L,9459438L,8473322L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,9459438L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,5265134L,5527290L,5527277L,5527226L,5527226L,5528046L,5527277L,5528250L,5528057L,5527277L,5527277L
};

private static final BsCalendar instance = new BsCalendar();

private static final TimeZone GMT = TimeZone.getTimeZone("GMT");

private static final long[] ENCODED_MONTH_LENGTHS = {
8673005L,5315258L,5314298L,9459438L,8673005L,5315258L,5314298L,9459438L,8473322L,5315258L,5314298L,9459438L,5327594L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,8673006L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,9459438L,8673005L,5315258L,5314490L,9459438L,8673005L,5315258L,5314298L,9459438L,8473325L,5315258L,5314298L,9459438L,5327594L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,9459438L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,5265134L,8673006L,5315258L,5314490L,9459438L,8673005L,5315258L,5314298L,9459438L,8669933L,5315258L,5314298L,9459438L,8473322L,5315258L,5314298L,9459438L,5327594L,5315258L,5314286L,9459438L,5315306L,5315258L,5265134L,8673006L,5315306L,5315258L,5265134L,5527290L,5527277L,5527226L,5527226L,5528046L,5527277L,5528250L,5528057L,5527277L,5527277L,
};
private static final long MS_PER_DAY = 86400000L;
private static final String[] _MONTH_NAMES = { "बैशाख", "जेठ", "असार", "साउन", "भदौ", "असोज", "कार्तिक", "मंसिर", "पौष", "माघ", "फाल्गुन", "चैत" };
public static final List<String> MONTH_NAMES = unmodifiableList(asList(_MONTH_NAMES));

Expand All @@ -40,14 +38,14 @@ public final class BsCalendar {

/**
* Magic numbers:
* 2000 <- the first year encoded in ENCODED_MONTH_LENGTHS
* BS_YEAR_ZERO <- the first year encoded in ENCODED_MONTH_LENGTHS
* month #5 <- this is the only month which has a day variation of more than 1
* & 3 <- this is a 2 bit mask, i.e. 0...011
*/
public int daysInMonth(int year, int month) throws BsException {
if(month < 1 || month > 12) throw new BsException(format("Month does not exist: %s", month));
try {
return 29 + (int) ((ENCODED_MONTH_LENGTHS[year - 2000] >>>
return 29 + (int) ((ENCODED_MONTH_LENGTHS[year - BS_YEAR_ZERO] >>>
(((month-1) << 1))) & 3);
} catch(ArrayIndexOutOfBoundsException ex) {
throw new BsException(format("Unsupported year/month combination: %s/%s", year, month));
Expand All @@ -57,16 +55,16 @@ public int daysInMonth(int year, int month) throws BsException {
public BsGregorianDate toGreg(BikramSambatDate bik) throws BsException {
int year = bik.year, month = bik.month, day = bik.day;

long timestamp = BS_EPOCH_TS;
long timestamp = BS_EPOCH_TS + (MS_PER_DAY * day);
month--;

while(year >= BS_YEAR_ZERO) {
while(month >= 1) {
while(--day >= 0) {
timestamp += MS_PER_DAY;
}
if(--month == 0) break;
day = daysInMonth(year, month);
while(month > 0) {
timestamp += (MS_PER_DAY * daysInMonth(year, month));
month--;
}
day = daysInMonth(--year, month = 12);
month = 12;
year--;
}

Calendar c = Calendar.getInstance(GMT);
Expand Down Expand Up @@ -99,11 +97,11 @@ public BikramSambatDate toBik(int year, int month, int day) throws BsException {
/**
* Magic numbers:
* 86400000 <- the number of miliseconds in a day
* 2007 <- The year (BS) whose first day is our Bikram Sambat Epoch (BSE)
* BS_YEAR_ZERO <- The year (BS) whose first day is our Bikram Sambat Epoch (BSE)
* -622359900000 <- unix timestamp of BSE ('1950-4-13')
*/
private BikramSambatDate toBik(String greg) throws BsException {
int year = 2007;
int year = BS_YEAR_ZERO;
int days;
try {
days = (int) Math.floor((parseDate(greg) - BS_EPOCH_TS) / MS_PER_DAY) + 1;
Expand Down
6 changes: 3 additions & 3 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/alxndrsn/bikram-sambat.js.git"
"url": "git+ssh://[email protected]/medic/bikram-sambat.git"
},
"keywords": [
"bikram",
Expand All @@ -22,9 +22,9 @@
"author": "alxndrsn",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/alxndrsn/bikram-sambat.js/issues"
"url": "https://github.com/medic/bikram-sambat/issues"
},
"homepage": "https://github.com/alxndrsn/bikram-sambat.js#readme",
"homepage": "https://github.com/medic/bikram-sambat#readme",
"devDependencies": {
"chai": "^3.5.0",
"grunt": "^1.0.1",
Expand Down
36 changes: 20 additions & 16 deletions js/src/index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
var toDevanagari = require('eurodigit/src/to_non_euro').devanagari;

var MS_PER_DAY = 86400000;
var MONTH_NAMES = ['बैशाख', 'जेठ', 'असार', 'साउन', 'भदौ', 'असोज', 'कार्तिक', 'मंसिर', 'पौष', 'माघ', 'फाल्गुन', 'चैत'];

// We have defined our own Epoch for Bikram Sambat: 1-1-2007 BS / 13-4-1950 AD
var BS_EPOCH_TS = -622359900000; // = Date.parse('1950-4-13')
var BS_YEAR_ZERO = 2007;
// ------ TO UPDATE THESE HARDCODED VALUES USE /scripts/encode-days-in-month.js
// We have defined our own Epoch for Bikram Sambat: 1970-1-1 BS or 1913-4-13 AD
var BS_EPOCH_TS = -1789990200000; // = Date.parse('1913-4-13')
var BS_YEAR_ZERO = 1970;
var ENCODED_MONTH_LENGTHS = [
5315258,5314490,9459438,8673005,5315258,5315066,9459438,8673005,5315258,5314298,9459438,5327594,5315258,5314298,9459438,5327594,5315258,5314286,9459438,5315306,5315258,5314286,8673006,5315306,5315258,5265134,8673006,5315258,5315258,9459438,8673005,5315258,5314298,9459438,8673005,5315258,5314298,9459438,8473322,5315258,5314298,9459438,5327594,5315258,5314298,9459438,5327594,5315258,5314286,8673006,5315306,5315258,5265134,8673006,5315306,5315258,9459438,8673005,5315258,5314490,9459438,8673005,5315258,5314298,9459438,8473325,5315258,5314298,9459438,5327594,5315258,5314298,9459438,5327594,5315258,5314286,9459438,5315306,5315258,5265134,8673006,5315306,5315258,5265134,8673006,5315258,5314490,9459438,8673005,5315258,5314298,9459438,8669933,5315258,5314298,9459438,8473322,5315258,5314298,9459438,5327594,5315258,5314286,9459438,5315306,5315258,5265134,8673006,5315306,5315258,5265134,5527290,5527277,5527226,5527226,5528046,5527277,5528250,5528057,5527277,5527277
];

// TODO this would be stored more efficiently converted to a string using
// TODO ENCODED_MONTH_LENGTHS would be stored more efficiently converted to a string using
// String.fromCharCode.apply(String, ENCODED_MONTH_LENGTHS), and extracted using
// ENC_MTH.charCodeAt(...). However, JS seems to do something weird with the
// top bits.
var ENCODED_MONTH_LENGTHS = [
8673005,5315258,5314298,9459438,8673005,5315258,5314298,9459438,8473322,5315258,5314298,9459438,5327594,5315258,5314298,9459438,5327594,5315258,5314286,8673006,5315306,5315258,5265134,8673006,5315306,5315258,9459438,8673005,5315258,5314490,9459438,8673005,5315258,5314298,9459438,8473325,5315258,5314298,9459438,5327594,5315258,5314298,9459438,5327594,5315258,5314286,9459438,5315306,5315258,5265134,8673006,5315306,5315258,5265134,8673006,5315258,5314490,9459438,8673005,5315258,5314298,9459438,8669933,5315258,5314298,9459438,8473322,5315258,5314298,9459438,5327594,5315258,5314286,9459438,5315306,5315258,5265134,8673006,5315306,5315258,5265134,5527290,5527277,5527226,5527226,5528046,5527277,5528250,5528057,5527277,5527277
],
MONTH_NAMES = ['बैशाख', 'जेठ', 'असार', 'साउन', 'भदौ', 'असोज', 'कार्तिक', 'मंसिर', 'पौष', 'माघ', 'फाल्गुन', 'चैत'];

/**
* Magic numbers:
* 2000 <- the first year (BS) encoded in ENCODED_MONTH_LENGTHS
* BS_YEAR_ZERO <- the first year (BS) encoded in ENCODED_MONTH_LENGTHS
* month #5 <- this is the only month which has a day variation of more than 1
* & 3 <- this is a 2 bit mask, i.e. 0...011
*/
function daysInMonth(year, month) {
if(month < 1 || month > 12) throw new Error('Invalid month value ' + month);
var delta = ENCODED_MONTH_LENGTHS[year - 2000];
var delta = ENCODED_MONTH_LENGTHS[year - BS_YEAR_ZERO];
if(typeof delta === 'undefined') throw new Error('No data for year: ' + year + ' BS');
return 29 + ((delta >>>
(((month-1) << 1))) & 3);
Expand Down Expand Up @@ -68,12 +68,16 @@ function toGreg(year, month, day) {
if(year < BS_YEAR_ZERO) throw new Error('Invalid year value ' + year);
if(day < 1 || day > daysInMonth(year, month)) throw new Error('Invalid day value', day);

var timestamp = BS_EPOCH_TS;
var timestamp = BS_EPOCH_TS + (MS_PER_DAY * day);
month--;

while(year >= BS_YEAR_ZERO) {
do while(day--) timestamp += MS_PER_DAY;
while(--month && (day = daysInMonth(year, month)));
day = daysInMonth(--year, month = 12);
while (year >= BS_YEAR_ZERO) {
while (month > 0) {
timestamp += (MS_PER_DAY * daysInMonth(year, month));
month--;
}
month = 12;
year--;
}

var d = new Date(timestamp);
Expand Down
2 changes: 1 addition & 1 deletion js/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('bikram-sambat', function() {
it('should throw Error if year is too small', () => {

assert.throw(() =>
bs.toGreg(2006, 1, 1));
bs.toGreg(1969, 1, 1));

});

Expand Down
62 changes: 62 additions & 0 deletions scripts/encode-days-in-month.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const daysInMonth = require('../test-data/daysInMonth.json');

/*
INSTRUCTIONS:
1) Update `/test-data/daysInMonth.json` as required
2) The first entry in `/test-data/daysInMonth.json` as the BS Epoch. Take this
and convert it to AD and update the bsEpoch constant below.
3) Run the script: node ./encode-days-in-month.js
4) Copy the output code into the files overwriting the existing hardcoded values
*/

// const bsEpoch = false; // eg: '1913-4-13';
const bsEpoch = '1913-4-13';

if (!bsEpoch) {
console.error('Read the instructions at the top of this file before executing.');
process.exit(1);
}

const processMonth = decimal => {
const delta = decimal - 29;
const binary = delta.toString(2);
const padded = binary.length === 1 ? '0' + binary : binary;
return padded;
};

const processYear = months => {
const binary = months.map(processMonth);
const combined = binary.reverse().join('');
const decimal = parseInt(combined, 2);
return decimal;
};

const encodedLengths = Object.values(daysInMonth).map(processYear);
const yearZero = Object.keys(daysInMonth)[0];
const bsEpochTs = Date.parse(bsEpoch);

console.log(`
// JavaScript - copy into js/src/index.js
// ------ TO UPDATE THESE HARDCODED VALUES USE /scripts/encode-days-in-month.js
// We have defined our own Epoch for Bikram Sambat: ${yearZero}-1-1 BS or ${bsEpoch} AD
var BS_EPOCH_TS = ${bsEpochTs}; // = Date.parse('${bsEpoch}')
var BS_YEAR_ZERO = ${yearZero};
var ENCODED_MONTH_LENGTHS = [
${encodedLengths.join(',')}
];
`);

console.log(`
// Java - copy into java/lib/src/main/java/bikramsambat/BsCalendar.java
// ------ TO UPDATE THESE HARDCODED VALUES USE /scripts/encode-days-in-month.js
// We have defined our own Epoch for Bikram Sambat: ${yearZero}-1-1 BS or ${bsEpoch} AD
private static final long BS_EPOCH_TS = ${bsEpochTs}L; // ${bsEpoch} AD
private static final int BS_YEAR_ZERO = ${yearZero};
private static final long[] ENCODED_MONTH_LENGTHS = {
${encodedLengths.map(length => length + 'L').join(',')}
};
`);
30 changes: 30 additions & 0 deletions test-data/daysInMonth.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
{
"1970": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1971": [31, 31, 32, 31, 32, 30, 30, 29, 30, 29, 30, 30],
"1972": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"1973": [30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
"1974": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1975": [31, 31, 32, 32, 30, 31, 30, 29, 30, 29, 30, 30],
"1976": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"1977": [30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
"1978": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1979": [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
"1980": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"1981": [31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
"1982": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1983": [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
"1984": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"1985": [31, 31, 31, 32, 31, 31, 29, 30, 30, 29, 30, 30],
"1986": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1987": [31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
"1988": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"1989": [31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
"1990": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1991": [31, 32, 31, 32, 31, 30, 30, 29, 30, 29, 30, 30],
"1992": [31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
"1993": [31, 31, 31, 32, 31, 31, 30, 29, 30, 29, 30, 30],
"1994": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1995": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 30],
"1996": [31, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
"1997": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1998": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"1999": [31, 32, 31, 32, 31, 30, 30, 30, 29, 29, 30, 31],
"2000": [30, 32, 31, 32, 31, 30, 30, 30, 29, 30, 29, 31],
"2001": [31, 31, 32, 31, 31, 31, 30, 29, 30, 29, 30, 30],
"2002": [31, 31, 32, 32, 31, 30, 30, 29, 30, 29, 30, 30],
Expand Down
1 change: 1 addition & 0 deletions test-data/toBik_euro.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"1913-04-13": "1970-01-01",
"1950-04-13": "2007-01-01",
"1950-04-14": "2007-01-02",
"1950-04-15": "2007-01-03",
Expand Down
12 changes: 12 additions & 0 deletions test-data/toGreg.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
[
{
"bs": [
1970,
1,
1
],
"expectedGreg": [
1913,
4,
13
]
},
{
"bs": [
2007,
Expand Down

0 comments on commit 77c090a

Please sign in to comment.