Skip to content

Instantly share code, notes, and snippets.

@rkdgusrnrlrl
Created March 19, 2025 15:13
Show Gist options
  • Select an option

  • Save rkdgusrnrlrl/79b2b28d95c51bf41e99a9c0b0cd597f to your computer and use it in GitHub Desktop.

Select an option

Save rkdgusrnrlrl/79b2b28d95c51bf41e99a9c0b0cd597f to your computer and use it in GitHub Desktop.
기간을 기준으로 기준 날짜, 비교할 날짜
import moment from 'moment';
import { adjustDateWithDuration } from './date-utils';
describe('date-utils', () => {
describe('adjustDateWithDuration', () => {
it('차이가 duration 이내인 경우 원래 targetDate를 유지해야 한다', () => {
// Arrange
const baseDate = '20250315'; // YYYYMMDD 형식
const targetDate = '20250325'; // 10일 차이
const duration = moment.duration(14, 'days'); // 14일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250325');
});
it('차이가 duration보다 큰 경우(양의 방향) 기준 날짜 + duration 값을 반환해야 한다', () => {
// Arrange
const baseDate = new Date(2025, 2, 15); // Date 객체 (2025-03-15)
const targetDate = new Date(2025, 3, 15); // Date 객체 (2025-04-15), 31일 차이
const duration = moment.duration(14, 'days'); // 14일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250329');
});
it('차이가 duration보다 큰 경우(음의 방향) 기준 날짜 - duration 값을 반환해야 한다', () => {
// Arrange
const baseDate = '20250415'; // YYYYMMDD 형식
const targetDate = '20250315'; // -31일 차이
const duration = moment.duration(-14, 'days'); // -14일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250401');
});
it('두 날짜가 동일한 경우 원래 날짜를 유지해야 한다', () => {
// Arrange
const baseDate = '20250315'; // YYYYMMDD 형식
const targetDate = '20250315'; // YYYYMMDD 형식, 0일 차이
const duration = moment.duration(14, 'days'); // 14일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250315');
});
it('duration이 0이면 항상 baseDate를 반환해야 한다', () => {
// Arrange
const baseDate = new Date(2025, 2, 15); // Date 객체 (2025-03-15)
const targetDate = new Date(2025, 3, 15); // Date 객체 (2025-04-15), 31일 차이
const duration = moment.duration(0); // 0일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250315');
});
it('복합 기간(년, 월, 일 등)도 처리할 수 있어야 한다', () => {
// Arrange
const baseDate = '20250315'; // YYYYMMDD 형식
const targetDate = '20260525'; // YYYYMMDD 형식, 1년 2개월 10일 차이
const duration = moment.duration({ months: 3, days: 15 }); // 3개월 15일 기간
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20250630');
});
it('음수 duration(년)을 처리할 수 있어야 한다', () => {
// Arrange
const baseDate = '20240102'; // YYYYMMDD 형식
const targetDate = '20221102'; // YYYYMMDD 형식, 1년 2개월 전
const duration = moment.duration({ years: -1, days: 1 }); // 1년 하루 전
// Act
const result = adjustDateWithDuration(baseDate, targetDate, duration);
// Assert
expect(result).toBe('20230103');
});
it('잘못된 형식의 날짜 문자열을 거부해야 한다', () => {
// Arrange
const baseDate = '20250315'; // 올바른 형식
const targetDate = '2025-03-15'; // 잘못된 형식 (하이픈 포함)
const duration = moment.duration(14, 'days');
// Act & Assert
expect(() => {
adjustDateWithDuration(baseDate, targetDate, duration);
}).toThrow('날짜는 YYYYMMDD 형식의 문자열 또는 Date 객체여야 합니다.');
});
it('moment.Duration이 아닌 duration을 거부해야 한다', () => {
// Arrange
const baseDate = '20250315';
const targetDate = '20250325';
const duration = { days: 14 } as any; // moment.Duration이 아님
// Act & Assert
expect(() => {
adjustDateWithDuration(baseDate, targetDate, duration);
}).toThrow('duration은 moment.Duration 객체여야 합니다.');
});
});
});
import moment from 'moment';
/**
* 'YYYYMMDD' 형식의 문자열인지 확인하는 함수
* @param value 확인할 값
* @returns 'YYYYMMDD' 형식이면 true, 아니면 false
*/
const isValidYYYYMMDD = (value: string): boolean => {
return /^\d{8}$/.test(value) && moment(value, 'YYYYMMDD', true).isValid();
};
/**
* 'YYYYMMDD' 문자열 또는 Date 객체를 받아 Moment 객체로 변환
* @param date 'YYYYMMDD' 형식의 문자열 또는 Date 객체
* @returns Moment 객체
* @throws 유효하지 않은 입력 형식일 경우 에러 발생
*/
const toMoment = (date: string | Date): moment.Moment => {
if (date instanceof Date) {
return moment(date);
}
if (typeof date === 'string' && isValidYYYYMMDD(date)) {
return moment(date, 'YYYYMMDD');
}
throw new Error('날짜는 YYYYMMDD 형식의 문자열 또는 Date 객체여야 합니다.');
};
/**
* 두 날짜와 기간(duration)을 받아서 조건에 따라 날짜를 조정하는 함수
* @param baseDate 기준 날짜 (YYYYMMDD 형식의 문자열 또는 Date 객체)
* @param targetDate 비교할 날짜 (YYYYMMDD 형식의 문자열 또는 Date 객체)
* @param duration 기간 (moment.Duration 객체)
* @returns 조정된 날짜 (YYYYMMDD 형식의 문자열)
* @throws 유효하지 않은 입력 형식일 경우 에러 발생
*/
export const adjustDateWithDuration = (
baseDate: string | Date,
targetDate: string | Date,
duration: moment.Duration
): string => {
// 입력된 날짜들을 Moment 객체로 변환
const base = toMoment(baseDate);
const target = toMoment(targetDate);
// duration이 moment.Duration 객체인지 확인
if (!moment.isDuration(duration)) {
throw new Error('duration은 moment.Duration 객체여야 합니다.');
}
// duration이 0이면 항상 baseDate 반환
if (duration.asMilliseconds() === 0) {
return base.format('YYYYMMDD');
}
// 두 날짜 사이의 difference를 moment duration으로 계산
const diffDuration = moment.duration(target.diff(base));
// 날짜 차이(절대값)와 입력된 duration(절대값)을 비교
if (Math.abs(diffDuration.asMilliseconds()) > Math.abs(duration.asMilliseconds())) {
// 날짜 차이가 duration보다 크면, 기준 날짜에 duration 적용
return base.clone().add(duration).format('YYYYMMDD');
}
// 날짜 차이가 duration 이내라면 원래 targetDate 반환
return target.format('YYYYMMDD');
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment