bbc / simorgh

The BBC's Open Source Web Application. Contributions welcome! Used on some of our biggest websites, e.g.
https://www.bbc.com/thai
Other
1.4k stars 224 forks source link

Investigate non-gregorian timestamps #1567

Closed dr3 closed 5 years ago

dr3 commented 5 years ago

Is your feature request related to a problem? Please describe. Following https://github.com/bbc/simorgh/issues/637 we need to add support for non-Gregorian datestamps, namely the Persian (Jalali/Khorshidi/Shamsi) Calendar. This calendar is required for both Persian and Pashto articles.

Describe the solution you'd like Add support to the timestamp container to allow the ability to toggle/extend the Gregorian date to include the Persian date for both Persian and Pashto.

If we decide to continue using MomentJs, we should aim to use a open source library such as https://www.npmjs.com/package/moment-jalaali, contributing to its repository if needed. If we go for a time stamp library without a popular Persian calendar library we should create one in a similar way, to ensure most possible reuse.

This should be paired on

Testing notes

Dev insight: Unit tests with snapshots, storybook and manual testing with persian speakers

Additional context Add any other context or screenshots about the feature request here.

dr3 commented 5 years ago

~Blocked on https://github.com/bbc/simorgh/issues/637~

j-pendlebury commented 5 years ago

We're now using MomentJS.

pjlee11 commented 5 years ago

Our previous Jalali implementation used the npm package jalaali-js which appears to be a dependency of the moment-jalaali package suggested in the description.

Below is the implementation in moment-ws using jalaali-js

https://github.com/bbc/moment-ws/blob/master/src/helpers/jalali.js

var moment = require('moment-timezone/moment-timezone'),
    jalali = require('jalaali-js');

function getJalaliString(gregorianMoment, jalaliMonths) {
    var jalaliDate = jalali.toJalaali(gregorianMoment.year(), gregorianMoment.month()+1, gregorianMoment.date());

    var output = jalaliDate.jd + ' ' + jalaliMonths[jalaliDate.jm-1] + ' ' + jalaliDate.jy;
    return output;
}

function addJalaliDate(locale, jalaliMonths, jalaliFormats, gregorianString) {
    var gregorianMoment = moment(gregorianString, jalaliFormats, locale, true);

    // gregorianString must be in one of jalaliFormats, and return an isValid moment for
    // Jalali calendar to be applied to - e.g this will exclude timeago timestamps
    if (gregorianMoment.isValid() && jalaliMonths.length === 12) {
        return gregorianString + ' - ' + getJalaliString(gregorianMoment, jalaliMonths);
    } else {
        return gregorianString;
    }
};

exports.addJalaliDate = addJalaliDate;

We should evaluate each package based on their API and consistency with the rest of the timestamp code.

On implementation ensure that the package we choose to use is only loaded in the persian bundle. And the pashto in the future.

https://bundlephobia.com/result?p=moment-jalaali@0.8.3 = 69.7KB MINIFIED + GZIPPED (assuming this has all the timezone logic included, and should have moment as a peerDep)

https://bundlephobia.com/result?p=jalaali-js@1.1.0 = 989B MINIFIED + GZIPPED

ibMadbouly commented 5 years ago

Findings

// defining sample gregorian date from JD Date Object. var gD = new Date();

console.log(JD.toJalali(gD)) ; // the Jalali_date from gregorian [] [ 1397, 10, 3 ]

// get the Jalali conversion from Gregorian one var jD = new JD(JD.toJalali(gD));

console.log(jD.format("dddd DD MMMM YYYY")); // چهارشنبه 16 امرداد 1398 // persian day | month | year


- **the library**  is easy to use gregorian to Jalali and vice versa but it  doesn't support Pashto Jalali calendar month names

#### Solution

- Option 1: The mapping of the Gregorian stamps to jalali ones should be added to the **psammead-timestamp-container**  based on locale passed and build new custom locals for Persin and Phasto and in this case we will not use the library **Jalali-date** and rely only on these custom defined locals like one used on WS here : https://github.com/bbc/moment-ws/tree/master/src/locale

- Option2 : to extend the **Jalali-date** to support Pashto dates the same way the library supports Persian 
dr3 commented 5 years ago

is there anyway this can be done that allows services that dont use Jalali to not include the to Jalali logic? would require importing stuff into their service bundle

ibMadbouly commented 5 years ago

Relying on the approach used by WS to support calendar conversion from gregorian to jalali here is the implementation of Persian locale fa. The Calendar conversions is done inside jalaliHelper based on the locale passed. This means we back to use the implementation of moment-ws

var jalaliHelper = require('../../helpers/jalali');

; (function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined'
        && typeof require === 'function' ? factory(require('moment-timezone/moment-timezone')) :
        typeof define === 'function' && define.amd ? define(['moment'], factory) :
            factory(global.moment)
}(this, function (moment) {
    'use strict';

    var LOCALE_CODE = 'fa',
        LL_FORMAT = 'D MMMM YYYY';

    // adding gregorian months in Persian language
    var gregorianMonths = [
        'ژانویه',
        'فوریه',
        'مارس',
        'آوریل',
        'ممکن است',
        'ژوئن',
        'جولای',
        'اوت',
        'سپتامبر',
        'اکتبر',
        'نوامبر',
        'دسامبر'
    ]; 
    var jalaliMonths = [
        'فروردین',
        'اردیبهشت',
        'خرداد',
        'تیر',
        'مرداد',
        'شهریور',
        'مهر',
        'آبان',
        'آذر',
        'دی',
        'بهمن',
        'اسفند'
    ];

    var jalaliFormats = [
        LL_FORMAT
    ];

    // Ensure base Persian locale data is loaded
    moment().locale(LOCALE_CODE);

    moment.defineLocale(LOCALE_CODE, {
        months: gregorianMonths,
        longDateFormat: { LL: LL_FORMAT },
        postformat: jalaliHelper.addJalaliDate.bind(null, LOCALE_CODE, jalaliMonths, jalaliFormats)
    });
}));
// test data 
var inputtedDates = [ 
        new Date (2019, 7, 9, 1, 1, 1, 1),
        new Date (2010, 1, 14, 15, 25, 50, 125),
        new Date (2018, 2, 20, 1, 1, 1, 1),
        new Date (2018, 2, 21, 1, 1, 1, 1),
        new Date (2021, 2, 20, 1, 1, 1, 1),
        new Date (2021, 2, 21, 1, 1, 1, 1)
    ]; 
    /*
       Gregorian and the corresponding Persian calendar month
        '9 اوت 2019 - 18 مرداد 1398',  
        '14 فوریه 2010 - 25 بهمن 1388',
        '20 مارس 2018 - 29 اسفند 1396',
        '21 مارس 2018 - 1 فروردین 1397',
        '20 مارس 2021 - 30 اسفند 1399',
        '21 مارس 2021 - 1 فروردین 1400'
    ]*/
dr3 commented 5 years ago

@ibMadbouly the moment implementation looks good. Could we do it like https://github.com/bbc/psammead/blob/latest/packages/utilities/psammead-locales/moment/yo.js where we use the default fa locale in psammead-locales/moment/fa.js, extend it, and use that in simorgh?

jamesdonoh commented 5 years ago

Summarising the decision on this:

Closing this investigation issue and will create separate issues to implement.