В одном из Pull-запросов моей команды я заметил, что date-fns был добавлен в качестве зависимости для нашей библиотеки компонентов для одного использования: преобразования временной метки в строку «MM/yy», поскольку она представляла собой дату истечения срока действия дебетовой карты.
Вдохновленный статьей You don’t (may not) need lodash/underscore, я подумал — не можем ли мы просто реализовать форматирование двузначного месяца и двузначного года? Это выглядит просто, верно?
Изучив родную документацию Date, у нас есть getMonth
— возвращает 0-индексированное целое число, представляющее месяц года. Первая загвоздка здесь — нужно увеличить значение, чтобы получить человекочитаемый месяц. Что касается года, у нас есть getFullYear
, что довольно самоочевидно.
Давайте напишем функцию — не обязательно для повторного использования, но скорее для того, чтобы мы могли легко ее протестировать. И давайте напишем самую глупую, и будем улучшать ее тест за тестом 😉
function expiration(date) {
return `${date.getMonth() + 1}/${date.getFullYear() % 100}`;
}
Это будет замечательно работать для чего-то вроде
it('should return 11/22 for nov 2022 expiration', () => {
expect(expiration(new Date(2022, 10)).toEqual('11/22');
});
но, конечно же, не сработает для однозначных месяцев
it('should return 03/23 for March 2022 expiration', () => {
expect(expiration(new Date(2023, 2)).toEqual('03/23');
});
// Expected "3/23" to equal "03/23"
Мы можем решить эту проблему с помощью забавного трюка: просто заполните заголовок 0
любым результатом, а затем сохраните два последних символа, используя отрицательный аргумент для substr
.
function expiration(date) {
return `0${date.getMonth() + 1}`.substr(-2) +
`/${date.getFullYear() % 100}`;
}
Кажется, закончили? Чего не хватает?
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Вы поняли. Что если остаток в date.getFullYear() % 100 равен одной цифре?
it('should return 03/00 for March 2100 expiration', () => {
expect(expiration(new Date(2100, 2)).toEqual('03/00');
});
// Expected "03/0" to equal "03/00"
что приводит нас к следующему
function expiration(date) {
return `0${date.getMonth() + 1}`.substr(-2) +
"/" +
`0${date.getFullYear() % 100}`.substr(-2);
}
Фух! Теперь осталось выяснить только одно: Что такое 00? 2000 или 2100?
Как далеко вы продвинулись в реализации функций, которые доступны в таких библиотеках, как date-fns
, lodash
?
Стоит ли это хлопот (и рисков!), учитывая, что tree shaking* делает отличную работу по упаковке оптимального производственного JavaScript?
И последнее, но не менее важное: дайте мне знать, если бы вы написали это по-другому. Не упустил ли я какой-нибудь угловой случай? Надеюсь, вам понравился мой подход, основанный на тестировании, хотя он ближе к «тестированию до», чем к «разработке, основанной на тестировании».
*при условии, что вы используете модулизацию кода и зависимостей (т.е. используете es-модули или другие реализации), и что бандлер способен использовать модулизацию для вырезания неиспользуемого кода.