2025年7月27日日曜日

P71 問62 (マグロウヒル大学演習シリーズ 微積分(上))を解く.

問62

 $\lim_{n \to \infty} |u_{n+1}/u_n| = |a| < 1$ ならば $\lim_{n \to \infty} u_n = 0$ であることを証明せよ.

証明の方針

 これは級数の収束判定で用いられるダランベールの収束判定法の証明の一部と同じ考え方を使います。

  1. $\lim_{n \to \infty} |u_{n+1}/u_n| = |a| < 1$ という条件から、十分大きな $n$ に対して $|u_{n+1}/u_n|$ が1より小さい定数 $r$ で上から抑えられることを示します。
  2. 具体的には、$|a| < r < 1$ となる $r$ を選びます。極限の定義から、ある番号 $N$ が存在し、$n \ge N$ ならば $|u_{n+1}/u_n| < r$ となることを利用します。
  3. この不等式を繰り返し使うと、$|u_n|$ が公比 $r$ の等比数列 $C \cdot r^n$ で抑えられることがわかります。
  4. $0 < r < 1$ なので $\lim_{n \to \infty} r^n = 0$ です。
  5. はさみうちの原理を使って $\lim_{n \to \infty} |u_n| = 0$ を導き、そこから $\lim_{n \to \infty} u_n = 0$ を示します。

解答

 $\lim_{n \to \infty} |u_{n+1}/u_n| = |a|$ であり、仮定より $|a| < 1$ です。

 ここで、$|a| < r < 1$ を満たす実数 $r$ を1つ選びます。(例えば $r = (|a| + 1) / 2$ とすればよいです。)

 $\epsilon = r - |a|$ とおくと、$\epsilon > 0$ です。 数列の極限の定義から、この $\epsilon$ に対して、ある自然数 $N$ が存在し、$n \ge N$ を満たすすべての $n$ について、 $$ | |u_{n+1}/u_n| - |a| | < \epsilon $$ が成り立ちます。この不等式は、 $$ |u_{n+1}/u_n| - |a| < \epsilon \iff |u_{n+1}/u_n| < |a| + \epsilon = |a| + (r - |a|) = r $$ を意味します。つまり、$n \ge N$ のとき $|u_{n+1}/u_n| < r$ が成り立ちます。

 $n > N$ のとき、この関係を繰り返し用いると、 $$ \begin{aligned} |u_n| &= \left| \frac{u_n}{u_{n-1}} \cdot \frac{u_{n-1}}{u_{n-2}} \cdot \dots \cdot \frac{u_{N+1}}{u_N} \cdot u_N \right| \\ &= \left|\frac{u_n}{u_{n-1}}\right| \cdot \left|\frac{u_{n-1}}{u_{n-2}}\right| \cdot \dots \cdot \left|\frac{u_{N+1}}{u_N}\right| \cdot |u_N| \\ &< r \cdot r \cdot \dots \cdot r \cdot |u_N| = r^{n-N} |u_N| \end{aligned} $$ となります。ここで $C = |u_N| / r^N$ とおくと、$C$ は正の定数であり、$|u_n| < C \cdot r^n$ と書けます。

 したがって、$n > N$ において $0 \le |u_n| < C \cdot r^n$ が成り立ちます。 $0 < r < 1$ なので $\lim_{n \to \infty} r^n = 0$ であり、よって $\lim_{n \to \infty} C \cdot r^n = 0$ です。

 はさみうちの原理より、$\lim_{n \to \infty} |u_n| = 0$ が得られます。 $-|u_n| \le u_n \le |u_n|$ であり、$\lim_{n \to \infty} |u_n| = 0$ かつ $\lim_{n \to \infty} -|u_n| = 0$ なので、再びはさみうちの原理より $\lim_{n \to \infty} u_n = 0$ となります。

2025年7月25日金曜日

P71 問66 (マグロウヒル大学演習シリーズ 微積分(上))を解く.

問66

 $\{ u_n \}$がフィボナッチ数列ならば,$\lim_{n \to \infty} \frac{u_{n+1}}{u_n} = \frac{1 + \sqrt{5}}{2}$ であることを証明せよ.

 フィボナッチ数列 $\{ u_n \}$ は $u_1=1, u_2=1, u_{n+2} = u_{n+1} + u_n ( n \ge 1 )$で定義されます.この問題の主張は,フィボナッチ数列のある項とその次の項の比が黄金比に収束するということです.等比数列ならばその比は一定だが,フィボナッチ数列はだんだんと黄金比になります.

証明の方針

 本問題の目標は,$u_{n+1}/u_n$ の極限が黄金比に等しいことを示すことです.すなわち,

  1. $a_n = u_{n+1}/u_n$ とおいたとき,$a_n$ が収束すること.
    • 部分列の単調性・有界性から,収束性を示します.
  2. $a_n$ の極限は黄金比であること.
    • 部分列の極限から,$a_n$の極限を特定します.

を示すことと同じです.

解答

 $a_n = u_{n+1}/u_n$ とおく.$a_n$ が単調増加もしくは単調減少で有界ならば収束するので,単調性を調べるために $a_{n+1} - a_n$ について考えたいが,うまく単調性を見い出せません.$a_1,a_2,a_3,...$ を計算してみると,振動しているようなので,部分列(偶数番目・奇数番目)の単調性からアプローチします.部分列がともに同じ値に収束すれば,数列もその値に収束するからです.

1. 部分列の単調性を示す.

 $a_n$ は振動しているので,以下で偶数番目と奇数番目の部分列の単調性を示します.

偶数番目の部分列 $a_{2k}$ の単調減少性を示す.

 偶数番目の部分列 $a_{2k}$ が単調減少であること、つまり $a_{2k+2} - a_{2k} < 0$ が成り立つことを示します。

$$ \begin{aligned} a_{2k+2} - a_{2k} &= \frac{u_{2k+3}}{u_{2k+2}} - \frac{u_{2k+1}}{u_{2k}} \\ &= \frac{u_{2k+3} u_{2k} - u_{2k+1} u_{2k+2}}{u_{2k+2} u_{2k}} \end{aligned} $$

ここで、分子 $u_{2k+3} u_{2k} - u_{2k+1} u_{2k+2}$ に着目し,フィボナッチ数列の漸化式 $u_n = u_{n-1} + u_{n-2}$ を用いて変形します.

$$ \begin{aligned} u_{2k+3} u_{2k} - u_{2k+1} u_{2k+2} &= (u_{2k+2} + u_{2k+1}) u_{2k} - u_{2k+1} u_{2k+2} \\ &= u_{2k+2} u_{2k} + u_{2k+1} u_{2k} - u_{2k+1} u_{2k+2} \\ &= u_{2k+2} u_{2k} - u_{2k+1} (u_{2k+2} - u_{2k}) \\ &= u_{2k+2} u_{2k} - u_{2k+1}^2 \end{aligned} $$

ここでカッシーニの恒等式を適用します.これはフィボナッチ数列の重要な性質です.

カッシーニの恒等式

$$u_{n+2} u_n - u_{n+1}^2 = (-1)^{n+1}$$

すると $u_{2k+2} u_{2k} - u_{2k+1}^2 = (-1)^{2k+1}$ なので,

$$ \begin{aligned} a_{2k+2} - a_{2k} &= \frac{(-1)^{2k+1}}{u_{2k+2} u_{2k}} \\ &= \frac{-1}{u_{2k+2} u_{2k}} < 0 \end{aligned} $$

となり,すなわち $a_{2k+2} - a_{2k} < 0$ なので,$a_{2k}$ の単調減少性が示されました.

奇数番目の部分列 $a_{2k-1}$ の単調増加性を示す.

 奇数番目の部分列 $a_{2k-1}$ が単調増加であること、つまり $a_{2k+1} - a_{2k-1} > 0$ が成り立つことを示します。偶数番目の部分列と同様にすると,

$$ a_{2k+1} - a_{2k-1} = \frac{1}{u_{2k+1} u_{2k-1}} > 0 $$

となり,すなわち $a_{2k+1} - a_{2k-1} > 0$ なので,$a_{2k-1}$ の単調増加性が示されました.

2. 部分列の有界性を示す.

偶数番目の部分列の有界性を示す.

 偶数番目の部分列 $a_{2k} = u_{2k+1} / u_{2k}$ は、すでに単調減少であることを示しました.したがって、この数列は最初の項 $a_2$ が最大値となります。$a_2 = u_3 / u_2 = 2 / 1 = 2$.ゆえに、$a_{2k} \le 2$ が常に成り立ちます。

 次に、この数列が下に有界であることを示します。ここで数列$a_n$は以下の関係式が成り立つことを使います.

$$ a_{n+1} - a_n = \frac{(-1)^{n+1}}{u_{n+1} u_n} $$

数列 $a_n$ の定義とカッシーニの恒等式から導くことができます.$(-1)^{n+1}$ の符号がポイントです.

$n$ が偶数の場合,つまり $n = 2k$ とすると,

$$ a_{2k+1} - a_{2k} = \frac{-1}{u_{2k+1} u_{2k}} < 0 $$

となり,$a_{2k+1} < a_{2k}$ が成り立ちます.これは、奇数番目の項は、直前の偶数番目の項より小さいことを意味します。(例: $a_3 < a_2, a_5 < a_4$ )

$n$ が奇数の場合,つまり $n = 2k - 1$ とすると,

$$ a_{2k} - a_{2k-1} = \frac{1}{u_{2k} u_{2k-1}} > 0 $$

となり,$a_{2k} > a_{2k-1}$が成り立ちます.これは、偶数番目の項は、直前の奇数番目の項より大きいことを意味します。(例: $a_2 > a_1, a_4 > a_3$ )

 これら2つの結果をまとめると,偶数番目の数列の項は前後の奇数番目の項より大きいということです.そして,奇数番目の部分列が単調増加で $1 \le a_{2k-1}$ であることを合わせると,$1 \le a_{2k-1} < a_{2k} \le 2$ なので,$1 < a_{2k} \le 2$ です.つまり,偶数番目の部分列は有界であることが示せました.

奇数番目の部分列の有界性を示す.

 偶数番目の部分列の有界性を示す中で,$1 \le a_{2k-1} < a_{2k} \le 2$ が明らかになりました.これはつまり,$1 \le a_{2k-1} < 2$ なので,奇数番目の部分列の有界性も示せました.

3. 極限を示す.

 偶数番目の部分列 $a_{2k}$ と 奇数番目の部分列 $a_{2k-1}$ はともに単調な数列で有界であることがわかりました.したがって,それぞれ収束します.そこで,$\lim_{k \to \infty} a_{2k} = L_e, \lim_{k \to \infty} a_{2k-1} = L_o$ とします.

極限が同一であること.

 数列 $a_n$ は以下の関係を満たすことが計算するとわかります.

$$ a_{n+1} = 1 + \frac{1}{a_n} $$

つまり,

$$ \begin{aligned} a_{2k+1} &= 1 + \frac{1}{a_{2k}} \\ a_{2k} &= 1 + \frac{1}{a_{2k-1}} \end{aligned} $$

となり,両辺,極限を取ると

$$ \begin{aligned} L_o &= 1 + \frac{1}{L_e} \\ L_e &= 1 + \frac{1}{L_o} \end{aligned} $$

となります.この連立方程式を解くと $L_o = L_e$ となります.極限が同じであることがわかりました.

極限を特定する.

 上の連立方程式から,共通の極限値を $L$ とすると

$$ L = 1 + \frac{1}{L} $$

が成り立っており,これを $L$ について解くと,$L$が正であることから,

$$ L = \frac{1 + \sqrt{5}}{2} $$

となります.

 以上より,$ \lim_{n \to \infty} \frac{u_{n+1}}{u_n} = \frac{1 + \sqrt{5}}{2}$ が示せました.

2025年7月24日木曜日

P71 問65 (マグロウヒル大学演習シリーズ 微積分(上))を解く.

問65

$$ \lim_{n \to \infty} n \sin(1/n) = 1 を証明せよ。 $$


 $ n \sin(1/n)$ の極限が $1$ であることを証明します。この証明では、関数の極限の基本的な性質と、幾何学的な考察に基づいて、はさみうちの原理で解きます。

関数の極限

求めたい極限は $\lim_{n \to \infty} n \sin(1/n)$ です。

ここで、$x = 1/n$ とおきます。$n \to \infty$ のとき、$x \to 0$ となります。特に、$n$ は自然数なので、$x$ は正の値を取りながら $0$ に近づきます($x \to 0^+$)。

したがって、与えられた数列の極限は、関数の極限として次のように書き換えられます。

$$ \lim_{n \to \infty} n \sin(1/n) = \lim_{x \to 0^+} \frac{1}{x} \sin(x) $$

これは次のように変形できます。

$$ \lim_{x \to 0^+} \frac{\sin(x)}{x} $$

幾何学的な考察とはさみうちの原理

この極限 $\lim_{x \to 0^+} \frac{\sin(x)}{x}$ が $1$ に等しいことを、幾何学的な考察とはさみうちの原理を用いて証明します。

$0 < x < \pi/2$ の範囲を考えます。単位円(半径 $1$ の円)において、中心を $O$、円周上の点を $A, B$ とします。点 $A=(1,0)$ とし、角 $AOB$ を $x$ ラジアンとします。また、点 $A$ から $x$ 軸に垂直に伸ばした線と、線分 $OB$ の延長線との交点を $C$ とします。

このとき、図形的に以下の面積の大小関係が成り立ちます。

三角形 $OAB$ の面積 $\le$ 扇形 $OAB$ の面積 $\le$ 三角形 $OAC$ の面積

それぞれの面積を計算すると:

  1. 三角形 $OAB$ の面積: 底辺 $1$、高さ $\sin x$ なので、$\frac{1}{2} \cdot 1 \cdot \sin x = \frac{1}{2} \sin x$
  2. 扇形 $OAB$ の面積: 半径 $1$、中心角 $x$ なので、$\frac{1}{2} \cdot 1^2 \cdot x = \frac{1}{2} x$
  3. 三角形 $OAC$ の面積: 底辺 $OA=1$、高さ $AC=\tan x$ なので、$\frac{1}{2} \cdot 1 \cdot \tan x = \frac{1}{2} \tan x$

これらの面積の大小関係を式で表すと、

$$ \frac{1}{2} \sin x \le \frac{1}{2} x \le \frac{1}{2} \tan x $$

この不等式全体を $2$ 倍すると、

$$ \sin x \le x \le \tan x $$

となります。

ここで、$0 < x < \pi/2$ では $\sin x > 0$ なので、この不等式全体を $\sin x$ で割ることができます。不等号の向きは変わりません。

$$ \frac{\sin x}{\sin x} \le \frac{x}{\sin x} \le \frac{\tan x}{\sin x} $$ $$ 1 \le \frac{x}{\sin x} \le \frac{\sin x / \cos x}{\sin x} $$ $$ 1 \le \frac{x}{\sin x} \le \frac{1}{\cos x} $$

次に、各辺の逆数を取ります。逆数を取ると不等号の向きが逆転することに注意してください。

$$ \frac{1}{1} \ge \frac{\sin x}{x} \ge \cos x $$

整理すると、

$$ \cos x \le \frac{\sin x}{x} \le 1 $$

となります。

ここで、$x \to 0^+$ の極限を考えます。

$$ \lim_{x \to 0^+} \cos x = \cos 0 = 1 $$

したがって、はさみうちの原理により、

$$ \lim_{x \to 0^+} \frac{\sin x}{x} = 1 $$

が導かれます。

結論

この結果を元の数列の極限に戻すと、

$$ \lim_{n \to \infty} n \sin(1/n) = \lim_{x \to 0^+} \frac{\sin(x)}{x} = 1 $$

したがって、$ n \sin(1/n)$ の極限は $1$ であることが証明されました。

2025年7月23日水曜日

P72 問67 (マグロウヒル大学演習シリーズ 微積分(上))を解く.

数列 $\left(1 + \frac{1}{n}\right)^{n+1}$ の単調性と極限

問67

$$u_n = \left(1 + \frac{1}{n}\right)^{n+1} n=1,2,3, \cdots $$ が単調減少数列であること、そしてその極限が $e$ であることを証明せよ.

1. 単調減少性の証明

数列 $u_n$ が単調減少であることを示すには、$u_{n+1} \le u_n$ が全ての $n \ge 1$ について成り立つことを証明すれば良いです。これは、比 $\frac{u_{n+1}}{u_n} \le 1$ を示すことと同義です。

まず、$u_n$ と $u_{n+1}$ の定義を書き出しましょう。 $$u_n = \left(1 + \frac{1}{n}\right)^{n+1} = \left(\frac{n+1}{n}\right)^{n+1}$$ $$u_{n+1} = \left(1 + \frac{1}{n+1}\right)^{n+2} = \left(\frac{n+2}{n+1}\right)^{n+2}$$

次に、$\frac{u_{n+1}}{u_n} \le 1$ が示すべき不等式、すなわち $u_{n+1} \le u_n$ を直接見ていきましょう。 $$\left(1 + \frac{1}{n+1}\right)^{n+2} \le \left(1 + \frac{1}{n}\right)^{n+1}$$

この不等式を変形してみます。 $$\frac{\left(1 + \frac{1}{n+1}\right)^{n+2}}{\left(1 + \frac{1}{n}\right)^{n+1}} \le 1$$ $$ \left(1 + \frac{1}{n+1}\right) \left( \frac{1 + \frac{1}{n+1}}{1 + \frac{1}{n}} \right)^{n+1} \le 1 $$ ここで、分数部分を計算します。 $$ \frac{1 + \frac{1}{n+1}}{1 + \frac{1}{n}} = \frac{\frac{n+2}{n+1}}{\frac{n+1}{n}} = \frac{n+2}{n+1} \cdot \frac{n}{n+1} = \frac{n(n+2)}{(n+1)^2} = \frac{n^2+2n}{n^2+2n+1} $$ よって、示すべき不等式は以下のようになります。 $$ \left(\frac{n+2}{n+1}\right) \left(\frac{n^2+2n}{n^2+2n+1}\right)^{n+1} \le 1 $$

上記で得られた不等式をさらに変形し、以下の形を考えます。 $$ \frac{n+2}{n+1} \le \left(\frac{n^2+2n+1}{n^2+2n}\right)^{n+1} $$ $$ \frac{n+2}{n+1} \le \left(1 + \frac{1}{n^2+2n}\right)^{n+1} $$

ここで、次の一般的な不等式を利用します。 任意の正の数 $x \neq 0$ と実数 $r>1$ に対して、$(1+x)^r > 1+rx$ が成り立ちます。(これはベルヌーイの不等式です.) $X = \frac{1}{n^2+2n}$、$R = n+1$ と置くと、$x > 0$、$r > 1$ です。 したがって、 $$ \begin{align*} \left(1 + \frac{1}{n^2+2n}\right)^{n+1} &> 1 + (n+1) \cdot \frac{1}{n^2+2n} \\ &= 1 + \frac{n+1}{n(n+2)} \\ &= \frac{n(n+2) + (n+1)}{n(n+2)} \\ &= \frac{n^2+2n+n+1}{n^2+2n} \\ &= \frac{n^2+3n+1}{n^2+2n} \end{align*} $$

これで、示したい不等式は次のようになります。 $$ \frac{n+2}{n+1} \le \frac{n^2+3n+1}{n^2+2n} $$ この不等式が真であることを確認します。両辺に分母を掛けて整理します。(分母は全て正なので不等号の向きは変わりません。) $$ (n+2)(n^2+2n) \le (n+1)(n^2+3n+1) $$ 左辺を展開します。 $$ n^3 + 2n^2 + 2n^2 + 4n = n^3 + 4n^2 + 4n $$ 右辺を展開します。 $$ n^3 + 3n^2 + n + n^2 + 3n + 1 = n^3 + 4n^2 + 4n + 1 $$ したがって、元の不等式は次のようになります。 $$ n^3 + 4n^2 + 4n \le n^3 + 4n^2 + 4n + 1 $$ この不等式は、$0 \le 1$ となり、明らかに全ての $n \ge 1$ に対して真です。

よって、我々が目指していた $$ \left(\frac{n+2}{n+1}\right) \left(\frac{n^2+2n}{n^2+2n+1}\right)^{n+1} \le 1 $$ すなわち $\frac{u_{n+1}}{u_n} \le 1$ が証明されました。これは $u_{n+1} \le u_n$ を意味し、数列 $u_n$ は単調減少であることを示しています。 ($n \ge 1$ においては常に厳密な不等号 $u_{n+1} < u_n$ が成り立ちます。)

2. 極限値の計算

次に、数列 $u_n$ の極限値を求めます。 $$\lim_{n \to \infty} u_n = \lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^{n+1}$$ この式は次のように変形できます。 $$\lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^{n+1} = \lim_{n \to \infty} \left[ \left(1 + \frac{1}{n}\right)^n \cdot \left(1 + \frac{1}{n}\right)^1 \right]$$

極限の性質を利用して、積の極限として分離します。 $$= \left( \lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^n \right) \cdot \left( \lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^1 \right)$$

ここで、自然対数の底 $e$ の定義から、$\lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^n = e$ です。 また、2番目の極限は容易に計算できます。 $$\lim_{n \to \infty} \left(1 + \frac{1}{n}\right)^1 = (1 + 0) = 1$$

したがって、 $$\lim_{n \to \infty} u_n = e \cdot 1 = e$$

数列 $u_n = \left(1 + \frac{1}{n}\right)^{n+1}$ は単調減少であり、その極限値は $e$ であることが証明されました。

2025年7月22日火曜日

P72 問68 (マグロウヒル大学演習シリーズ 微積分(上))を解く.

問68

 すべての $n>=N$ について $a_n \ge b_n$ であり、かつ $\lim_{n \to \infty} a_n = A$ および $\lim_{n \to \infty} b_n = B$ が成り立つならば、$A \ge B$ である。

解答の方針

この証明は、「$A-B \ge 0$ を示す」という方針で進めます。

証明

まず、$c_n$ という新しい数列を次のように定義します。

$$c_n = a_n - b_n$$

与えられた条件は以下の3つです。

  1. すべての $n>=N$ に対して $a_n \ge b_n$
  2. $\lim_{n \to \infty} a_n = A$
  3. $\lim_{n \to \infty} b_n = B$

条件1に着目すると、$a_n \ge b_n$ であるため、両辺から $b_n$ を引くと、$a_n - b_n \ge 0$ となります。
これは、私たちが定義した数列 $c_n$ が常に非負であることを意味します。

つまり、すべての $n>=N$ に対して $c_n \ge 0$ が成り立ちます。

次に、この数列 $c_n$ の極限を調べてみましょう。
極限の線形性(和と差の極限はそれぞれの極限の和と差になる性質)を利用します。

$$\lim_{n \to \infty} c_n = \lim_{n \to \infty} (a_n - b_n)$$

与えられた条件2と3を適用すると、以下のようになります。

$$\lim_{n \to \infty} (a_n - b_n) = \lim_{n \to \infty} a_n - \lim_{n \to \infty} b_n$$

したがって、

$$\lim_{n \to \infty} c_n = A - B$$

ここで、重要な定理を思い出しましょう。
「もし数列 $c_n$ がすべての項で $c_n \ge 0$ であり、かつその極限が存在して $L$ であるならば、その極限 $L$ もまた $L \ge 0$ である」という定理です。

私たちの数列 $c_n$ は、すべての $n$ で $c_n \ge 0$ であり、その極限は $A - B$ です。
この定理を適用すると、次が成り立たなければなりません。

$$A - B \ge 0$$

この不等式の両辺に $B$ を加えることで、最終的な結論が得られます。

$$A \ge B$$

まとめ

この証明により、もし2つの数列が常に片方がもう片方以上であるという関係を保ちながら収束するならば、それぞれの極限値の間にも同じ大小関係が成り立つことが示されました。これは、極限操作が不等式関係を保存する良い例であり、解析学において非常に基本的な性質の一つです。

この内容が、皆さんの数学の理解に役立てば幸いです。

2021年10月3日日曜日

【moto】motoでtransact_write_itemsをモックする。【Python】

motoでtransact_write_itemsをモックする。

moto と transact_write_items で検索すると、motoを拡張してモックする方法が上位に表示される。詳しく調べていないが、moto 2.2.4 では拡張せずともモックできるので、サンプルを提示したい。

この記事ではテストコードは一部だけ記載するので、完全なものは https://github.com/tomohikoseven/moto_transact_write_items を参照ください。

実装コード

実装したコードは、ClickCountというテーブルにItemをトランザクションで追加・更新(アトミックカウンタ)するものです。DynamoDBをご存知の方には、簡単なものかと思います。

簡単に説明すると、以下の実装コードはハッシュキー:month は文字列 YYYYMM を、レンジキー(ソートキー):date は文字列 YYYYMMDD を期待して、1トランザクションで2つのItemに対し、clickCountをカウントUPするものになります。

トランザクションの1つ目は日付の末尾が00となっており、月間のclickCountをカウントUPします。今回の記事では気にすることではないです。


(index.py)
import boto3


client = boto3.client('dynamodb',  region_name='ap-northeast-1')
def lambda_handler( month:str, date:str ):
  try:
    response = client.transact_write_items(
      TransactItems=[
        {
          'Update' : {
            'Key' : {
              'hKey': { 'S': month },
              'rKey': { 'S': month + '00' }
            },
            'TableName':'ClickCount',
            'UpdateExpression': 'ADD clickCount :increment',
            'ExpressionAttributeValues' : {
              ':increment': { 'N': '1' }
            }
          }
        },
        {
          'Update' : {
            'Key' : {
              'hKey': { 'S': month },
              'rKey': { 'S': date }
            },
            'TableName':'ClickCount',
            'UpdateExpression': 'ADD clickCount :increment',
            'ExpressionAttributeValues' : {
              ':increment': { 'N': '1' }
            }
          }
        }
      ]
    )
  except Exception as e :
    print("===")
    print(e)
    raise e

  return 200

テストコード

pytestでテストコードを実行します。

motoでモックするテストコードを記述したことがある方は難しいことはないです。mock_dynamodb2をimportして、アノテーション@mock_dynamodb2をテストケースに付けてやればよいです。


(test_index.py)

# 本当のテストは1ケースだけではないので、ここでは余分なライブラリがインポートされています。
from importlib import import_module
import boto3
from moto import mock_dynamodb2
from unittest import mock
from botocore.exceptions import ClientError
import pytest


@mock_dynamodb2
def test_success(set_dynamodb_Count):
  # mock dynamodb
  table = set_dynamodb_Count()

  month = '202101'
  date = '20210101'
  from src.index import lambda_handler
  res = lambda_handler( month, date )

  item = table.get_item(Key={'hKey':month, 'rKey': date})
  assert res == 200
  assert item['Item']['clickCount'] == 1

conftest.pyに set_dynamodb_Count を記載しています。テストにはこちらも必要になります。test_index.pyと同一フォルダに置いてください。


(conftest.py)
import pytest
import boto3

@pytest.fixture
def set_dynamodb_Count():
  def definition():
    dynamodb = boto3.resource('dynamodb', region_name='ap-northeast-1')
    dynamodb.create_table(
      TableName='ClickCount',
      KeySchema=[
          {
              'AttributeName': 'hKey',
              'KeyType': 'HASH'
          },
          {
              'AttributeName': 'rKey',
              'KeyType': 'RANGE'
          }
      ],
      AttributeDefinitions=[
          {
              'AttributeName': 'hKey',
              'AttributeType': 'S'
          },
          {
              'AttributeName': 'rKey',
              'AttributeType': 'S'
          }
      ],
    )

    return dynamodb.Table('ClickCount')
  return definition

2021年8月11日水曜日

【HTML】過去入力したメールアドレスのサジェスト表示の方法

<input id="email" type="email" />

id="email" を付けないと、フィールドにフォーカスを当てたときにサジェスト表示されない。

passwordだと、パスワード保存設定(Chrome系のブラウザの設定)だったり、iCloudキーチェーンの有効化設定+パスワード自動入力設定を有効化しなければならないが、メールアドレスに関してはその必要がないみたい。


【正規表現】連続する同一文字を含む文字列かどうかを判断する。【JavaScript】


var continueChar = function( str ) {
  const regexp = /(.)\1{3,}/;   // 4つ連続で同一文字を含む
  return regexp.test( str );
};

exports.continueChar = continueChar;

2021年7月21日水曜日

【FIRE前】2021年7月の支出

夫婦2人+猫1匹の支出

項目支出額備考
家賃7.5万円
食費4.8万円
電気・ガス・水道0.0万円給与日が早く、支払い前なので、0万円。
日用品0.3万円
0.4万円
整髪0.0万円
医療0.0万円
通信費0.7万円
お小遣い1.9万円夫婦二人分
寄付0.28万円子供食堂へ寄付。
生命保険料0.2万円
その他0.6万円書籍代とか。
合計16.68万円

下記書籍をベースとして、早期リタイアを目指しています。

2021年7月2日金曜日

【FIRE前】FIRE後の国民年金の支払いをどうするか。

 夫婦2人+猫1匹で生活をしています(FIRE前)。

FIREした後、支出の中でなかなかの割合を占めるものがあります。国民年金です。

一人あたり約1.6万円なので、夫婦であれば年間約38.4万円の支出です。

未来のための投資と考えれば、まあよいかなと思う反面、米国ETF買っていた方がよいのでは?とも思います。(最終的には米国インデックスETFの方がお金がよりもらえます。)

結論から言えば、国民年金は全納もしくは免除(全額、4分の3、半額、4分の1)で未納期間を作らないようにしたいです。

その訳は、障害年金をもらうための保険としてです。
ずっと健康ではなく、障害を負ってしまうかもしれないので、そのための対策です。
障害年金をもらうための納付要件は以下です。(引用元
  1. 保険料納付要件の原則は加入期間の3分の2以上納めていること。
  2. 1.を満たさない場合は、直近1年間に滞納期間がないこと。
例えば40歳でFIRE(完全リタイア)し、国民年金を納めない場合、50歳まではよいとして、それ以降に障害を負ってしまうと加入期間が3分の2未満となり、障害年金がもらえません。障害となってしまってしかもお金がもらえないとなると、二重の痛手です。

よって、FIRE前の現在の結論としては未納期間を作らないようにします。

下記書籍をベースとして、早期リタイアを目指しています。

2021年6月28日月曜日

【FIRE前】何をポイントにお金が貯まっていったのか。

長期的にFIREを目指していたわけではないのですが、7年くらい共働きで働くうちに、貯金が貯まっていきました。この期間は、夫婦2人の生活において以下の決まり事を守っていました。
  • 家は買わない。買うとしても老後に現金一括で購入。
  • 保険は最低限の保証のもののみ。一人1000円/月程度のもの。
  • 車は買わない。
  • 結婚式は挙げない。写真のみ。
  • 所得が低い方(私)を生活費とする。
  • 急な支出は生活費以外から。
結果的に、借金をしない・固定費を抑えるような生活になったのかもしれません。
ただ、
  • 新婚旅行はハワイへ
この一度だけは大きな出費をしました。

これからFIREをする上で、今より1万円くらい安い賃貸物件(月6万円ほど)に引っ越すつもりです。



下記書籍をベースとして、早期リタイアを目指しています。

2021年6月27日日曜日

【FIRE前】FIRE時の金融資産目標はどうするか。

 4%ルールの原論によれば、株式:75%、債券:25%の割合が成功確率が一番いいわけですが、株式:100%と現金(生活費2年分)でFIREしようとしてます。

別のサイトによると、4%ルールをより長期間に適用した結果は、株式100%の方が高確率で成功します。(以下の画像を参照。)

(引用:Early Retirement Now

つまり、株式100%でFIREしようとしています

【FIRE時の目標資産】
項目目標額備考
VOO6000万円楽天証券では、手数料無料で購入可能。
現金600万円ほど株式市場が暴落した際に生活費を下ろす。2年分程度。

なぜ、VOOかというとバンガード社の人気米国株ETFの中で、インデックスで成績がよいものだったためです。2020/07現在、VOOの目標額の83%を達成しています。

VOO以外の株(個別株、米国ETF)を新型コロナによる株式市場暴落時に仕込んでいましたので、それらを売却してVOOに付け替えていきたいと思います。


下記書籍をベースとして、早期リタイアを目指しています。

2021年6月26日土曜日

【FIRE前】2021年6月の支出

夫婦2人+猫1匹の6月の支出

項目支出額備考
家賃7.5万円
食費4.7万円
電気・ガス・水道0.6万円水道代は含まず。
日用品0.65万円
0.4万円
整髪0.1万円
医療0.38万円歯医者代
通信費1.16万円契約事務手数料5000円含む。後で戻ってくる。
お小遣い1.5万円夫婦二人分
寄付0.28万円子供食堂へ寄付。
生命保険料0.2万円
交通費0.15万円
その他0.1万円書籍代とか。
合計17.72万円

下記書籍をベースとして、早期リタイアを目指しています。

【FIRE前】朝食、昼食、夕食は何を食べているか。

 FIRE前ですが、お弁当や総菜ではなく、自炊をするように心掛けています。


【朝食】フルグラ+コーンフレーク+オートミール(インスタント)+豆乳(低脂肪)。

【昼食】野菜+豚肉たっぷりの味噌汁のみ。

【夕食】野菜+豚肉たっぷりの炒め物。もしくは麺類(袋ラーメン、そうめんなど)。

だいたいこのようなもの。炒め物の場合、ご飯を付けたりもします。

食費はだいたい楽天Edyで払うのですが、マネーフォワードで見ると月4万円使っていました。おー、使っているな。

フルグラ

オートミール(インスタント)

2021年6月22日火曜日

【FIRE前】FIRE時の年間支出目標

 夫婦2人(子供なし、猫1匹)40代でのFIRE時の年間支出目標


項目目標額備考
家賃80万円月6万円、更新料、火災保険を含む
食費36万円月3万円
電気・ガス・水道14.4万円月1.2万円
日用品4.8万円月0.4万円
4.8万円月0.4万円
整髪2.4万円月0.2万円
医療4.8万円月0.2万円
通信費12万円携帯:0.5万円/二人、インターネット:0.5万円
交通費7万円主に帰省代
お小遣い24万円月1万円/一人
国民健康保険4万円減免7割適用を仮定。
所得税・住民税0万円
国民年金0~39.8万円約1.6万円/月/一人。余裕がないときは延納。
合計204~234万円

2021年5月30日日曜日

【JavaScript】Day.jsでの日付妥当性をisValid()で。

Day.jsで日付の妥当性は isValid() を使うとできます。しかし、検索をすると使えない(日付の検証には使えない。)と結果が出てきます。それは誤りで、plugin を利用すれば可能です。customParseFormat を追加しましょう。

ポイント

  • プラグイン customParseFormat を追加。
  • dayjs() の第3引数 strict に true を指定。
const dayjs = require('dayjs');

// プラグイン 追加
const customParseFormat = require('dayjs/plugin/customParseFormat');
dayjs.extend(customParseFormat);

console.log( dayjs( '20210102', 'YYYYMMDD' ).isValid() ); // => true
console.log( dayjs( '20210132', 'YYYYMMDD' ).isValid() ); // => true
console.log( dayjs( '20210102', 'YYYYMMDD', true ).isValid() ); // => true
console.log( dayjs( '20210132', 'YYYYMMDD', true ).isValid() ); // => false
console.log( dayjs( '2021013a', 'YYYYMMDD', true ).isValid() ); // => false

【JavaScript】Day.jsのAPIサンプル(Parse編)

 Day.jsの使い方を知るためにサンプルを作っています。

Parse

 日付や時刻を含む文字列や数値からオブジェクトを作成するものです。作成されたオブジェクトはUTC時間だったりするので、利用時は気を付けないと想定とずれた時間になります。

Parse

 テキストをオブジェクトに変換します。日本語の日付を使うなどする場合など、「Invalid Date」でエラーとなってしまうときは、CustomParseFormat プラグインを利用することも頭に入れておくとよいです。

const dayjs = require('dayjs');

// JST:2021-04-30 00:00:00
// UTC:2021-04-29 15:00:00 (日本との差は-9時間のため)
console.log( dayjs('2021-04-30', 'YYYY-MM-DD').toDate() );
// => 2021-04-29T15:00:00.000Z (UTC)
console.log( dayjs('2021年04月30日', 'YYYY年MM月DD日').toDate() );
// => Invalid Date (日本語があるとパースできない.)

// プラグインを追加 
const CustomParseFormat = require('dayjs/plugin/customParseFormat');
dayjs.extend(CustomParseFormat);

console.log( dayjs('2021年04月30日', 'YYYY年MM月DD日').toDate() );
// => 2021-04-29T15:00:00.000Z (日本語があってもパースできる.)
console.log( dayjs('2021年04月30日', 'YYYY年MM月DD日').locale('ja').format() );
// => 2021-04-30T00:00:00+09:00 (日本でロケールしてみただけ.)

Now

 現在時刻のオブジェクト取得します。日本時間にするには ロケール変換+format() します。

const dayjs = require('dayjs');

let now_utc = dayjs();
let now_jst = dayjs().locale('ja');
console.log( now_utc.toDate() );
// => 2021-04-30T00:09:31.085Z (こちらは0時)
console.log( now_jst.format() );
// => 2021-04-30T09:09:31+09:00 (こちらは9時. toDate() はUTCになってしまうので注意.)

String

 ISO 8601形式の文字列からオブジェクトを作成します。日付と時刻の間に"T"を付け、時刻の後ろに"Z"や"+0900"などを付与した文字列です。

const dayjs = require('dayjs');

let utc = dayjs('2021-04-15T15:00:00.000Z');
console.log( utc.toDate() );

let jst = dayjs('2021-04-15T15:00:00.000+0900');
console.log( jst.locale('ja').format() ); // toDate()はUTC時間で出力される

String + Format

 事前に日付のフォーマットわかっていれば、引数の日付の文字列からオブジェクトが作成できます。注意すべきは CustomParseFormat プラグインを使わなければいけないことです。補足ですが、dayjs()の第4引数に真偽を指定すると、結果が変わります。

const dayjs = require('dayjs');
// プラグイン
const customParseFormat = require('dayjs/plugin/customParseFormat');

dayjs.extend(customParseFormat); // プラグイン追加
let jst = dayjs('2021年10月18日', 'YYYY年MM月DD日', 'ja');
console.log(jst.locale('ja').format());

let jst_imp = dayjs('2021年10月32日', 'YYYY年MM月DD日', 'ja');
let jst_strict = dayjs('2021年10月32日', 'YYYY年MM月DD日', 'ja', true);

console.log(jst_imp.locale('ja').format());
// => 2021-11-01T00:00:00+09:00  第4引数がtrueでない場合、日付の繰り越しが起こる.
console.log(jst_strict.locale('ja').format());
// => Invalid Date 第4引数がtrueの場合、日付の厳密性がチェックされ、存在しない日付の場合、
エラー(Invalid Date)になる.

 日付のフォーマットが1つではなく複数指定される可能性がある場合でもオブジェクト作成できます。日付のフォーマットを配列で指定してあげます。

const dayjs = require('dayjs');
const customParseFormat = require('dayjs/plugin/customParseFormat');

dayjs.extend(customParseFormat);
let date  = dayjs("2001-12-25", ["YYYY", "YYYY-MM-DD"], 'ja', true);
let date2 = dayjs("2001", ["YYYY", "YYYY-MM-DD"], 'ja', true);
console.log(date.locale('ja').format());  // => 2001-12-25T00:00:00+09:00
console.log(date2.locale('ja').format()); // => 2001-01-01T00:00:00+09:00


let date3 = dayjs("2001-25-12", ["YYYY", "YYYY-MM-DD"], 'ja', true);
let date4 = dayjs("2001-25-12", ["YYYY", "YYYY-MM-DD"], 'ja');
console.log(date3.locale('ja').format());  // => Invalid Date
console.log(date4.locale('ja').format());  // => 2001-01-01T00:00:00+09:00

Unix Timestamp (milliseconds)

 ミリ秒を表す数値を指定することでオブジェクトを作成します。基準日は1970年1月1日午前0時です。引数に指定したミリ秒だけ加算された日時のオブジェクトが作成されます。

const dayjs = require('dayjs');

let date = dayjs(0);
console.log( date.toDate() );
// => 1970-01-01T00:00:00.000Z

let date1 = dayjs(1);
console.log( date1.toDate() );
// => 1970-01-01T00:00:00.001Z

Unix Timestamp (seconds)

 秒を表す数値を指定し、オブジェクトを作成します。基準日は1970年1月1日午前0時です。引数に指定した秒だけ加算された日時のオブジェクトが作成されます。

const dayjs = require('dayjs');

let date = dayjs.unix(0);
console.log( date.toDate() );
// => 1970-01-01T00:00:00.000Z

let date2 = dayjs.unix(1);
console.log( date2.toDate() );
// => 1970-01-01T00:00:01.000Z dayjs()をラップしている.

let date3 = dayjs( 1 * 1000 );
console.log( date3.toDate() );
// => 1970-01-01T00:00:01.000Z 

Date

 JavaScriptのDateオブジェクトからオブジェクトを作成します。Dateオブジェクトのクローンを返すため、元のDateオブジェクトを変更しても、Day.jsオブジェクトは変更されません。その逆も同様です。

const dayjs = require('dayjs');

var d = new Date(2019, 7, 18); // monthは0-11を指定し、1月-12月を扱う罠.
var day = dayjs(d);

console.log( d );  // => 2019-08-17T15:00:00.000Z
console.log( day.toDate() );  // => 2019-08-17T15:00:00.000

Object

 オブジェクトからDay.jsオブジェクトを作成します。ObjectSupport プラグインを追加して使用します。

const dayjs = require('dayjs');
const ObjectSupport = require('dayjs/plugin/objectSupport'); // プラグイン

// プラグイン追加.
dayjs.extend(ObjectSupport);

let now = dayjs();
let date = dayjs({ hour:15, minute:10 });
console.log( now.toDate() );
// => 2021-05-01T07:51:36.994Z
console.log( date.toDate() );
// => 2021-05-01T06:10:00.000Z
// 当日の日本時間15時10分を指す?。UTCにすると9時間前なので、6時10分となる。

now = dayjs();
let date2 = dayjs({});  // 空のオブジェクトは現在日時
console.log( now.toDate() );
// => 2021-05-01T07:54:59.148Z
console.log( date2.toDate() );
// => 2021-05-01T07:54:59.148Z

let date3 = dayjs({ year :2010, month :3, day :5, hour :15, minute :10, second :3, millisecond :123});
console.log( date3.toDate() );
// => 2010-04-05T06:10:03.123Z
// monthは0-11の整数で、1月-12月を表す.

Array

 配列からDay.jsオブジェクトを作成します。arraySupport プラグインを追加する必要があります。dayjs([]) は現在時刻です。

const dayjs = require('dayjs');
const ArraySupport = require('dayjs/plugin/arraySupport');

dayjs.extend(ArraySupport);

// [ 年、月、日、時、分、秒、ミリ秒 ]
let date = dayjs([2010, 1, 14, 15, 25, 50, 125]);
console.log( date.toDate() );
// => 2010-02-14T06:25:50.125Z
// month部分は0-11を指定することで、1月-12月を表します。
// ログ出力ではUTC時間になるため、時刻が9時間前となります。 

let date2 = dayjs([2010, 6]);     // July 1st
console.log( date2.toDate() );
// => 2010-06-30T15:00:00.000Z
// month:6 は7月のこと。7/1 00:00:00から-9時間となり、6/30 15:00:00となる。 
// UTC時間は面倒だ。

UTC

 UTC時間のDay.jsオブジェクトを作成します。

const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');

dayjs.extend(utc);

let date = dayjs();
console.log( date.format() );
// => 2021-05-02T09:10:25+09:00
// 日本で実行しているので、format()は日本時間が出力される。
let date_utc = dayjs().utc();
console.log( date_utc.format() );
// => 2021-05-02T00:10:25Z
// 日本で実行していても、format()はUTC時間で出力される。 

Dayjs Clone

 Day.jsオブジェクトを複製して、同じオブジェクトを作成します。2種類の方法があり、複製元からclone()を使うか、dayjs()の引数に複製元を渡すかです。

const dayjs = require('dayjs');

let a = dayjs();
let b = a.clone();

console.log( a.toDate() ); // => 2021-05-02T00:18:31.274Z
console.log( b.toDate() ); // => 2021-05-02T00:18:31.274Z

a = dayjs();
b = dayjs(a);

console.log( a.toDate() ); // => 2021-05-02T00:18:31.325Z
console.log( b.toDate() ); // => 2021-05-02T00:18:31.325Z

Validation

 日付が正しいかチェックします。customParseFormat プラグインを使用します。注意しなければならないのは、dayjs()の第3引数の真偽です。厳密に日付の正しさを求めるならば true を指定します。そうでない場合、日付が正しくない場合でも繰り上げなどの計算をしてくれます。

const dayjs = require('dayjs');
// プラグイン
const customParseFormat = require('dayjs/plugin/customParseFormat');

// プラグインを追加 
dayjs.extend(customParseFormat);

let date = dayjs('2021-03-19', 'YYYY-MM-DD', true);
console.log( date.isValid() ); // => true

let date2 = dayjs('2021-15-19', 'YYYY-MM-DD', true);
console.log( date2.isValid() ); // => false

// 日付の形式が合っていればOKのやりかた。
let date3 = dayjs('2021-13-19', 'YYYY-MM-DD');
console.log( date4.isValid() ); // => true. 日付の形式が合っていればOKとなる。
console.log( date3.toDate() );
// => 2022-01-18T15:00:00.000Z
// 日付を勝手に繰り上げする.

【JavaScript】MS932で文字を判断する。

JavaScriptを使って、MS932文字コードで文字を判断します。ライブラリは iconv-lite を利用。


const iconv = require('iconv-lite');

let buf = iconv.encode('亜', 'ms932' );
console.log( buf );  //=> <Buffer 88 9f>  [0x88, 0x9f]の意味。

// MS932の亜に対応した数値。8ビットシフトするのがポイント。
let enc = buf[0] << 8 + buf[1];  // 0x889f 
if ( enc === 0x889f ){
  console.log('亜だね。');
}

【JavaScript】Day.jsで年齢計算をする。

 基準日時点で生年月日から年齢はいくつかを計算します。


  let base = dayjs(baseDate, YYYYMMDD, true);  // 基準日
  let birth = dayjs(birthday, YYYYMMDD, true); // 生年月日 


  let ibase = Number( base.format(YYYYMMDD) );
  let ibirth = Number( birth.format(YYYYMMDD) );

  return Math.floor( (ibase - ibirth)/10000 );

【JavaScript】Day.jsで日付演算処理を行う。

 元となるソースはGitHub上にあります。

共通

 共通的に必要となる定義を示します。フォーマット変換するので、プラグインを入れるのがポイントです。


const dayjs = require("dayjs");
// プラグインの追加
dayjs.extend(require("dayjs/plugin/customParseFormat"));

// フォーマット
const YYYYMMDD = "YYYYMMDD";
const YYYYMMDD_HHmmss = "YYYYMMDD HHmmss";
const YYYYMMDD_HHmmss_SSS = "YYYYMMDD HHmmss.SSS";

エラー判定処理

 Day.jsの癖として、日付処理ができない場合は「Invalid Date」という文字列を返します(細かいことはわかりませんが。)。それを利用したエラー判定を行います。これは下で示していく自作APIが処理エラー時にnullを返したいために作られました。


function isValidDate(day) {
  if (typeof day === "string") {
    return day === "Invalid Date" ? null : day;
  }

  return day.isValid() ? day : null;
}

年齢計算

 diff()のポイントは、基準日に対し過去が正の値・未来が負の値となることです。つまり基準日.diff(過去日) > 0 で、基準日.diff(未来日) < 0 です。


/**
 * 年齢計算
 * @param {string} baseDate  年齢計算の基準日 YYYYMMDD
 * @param {string} birthday  生年月日 YYYYMMDD
 * @returns 満年齢 or null(計算不可).誕生日に年をとる.
 */
exports.calcAge = (baseDate, birthday) => {
  let base = dayjs(baseDate, YYYYMMDD, true);
  let birth = dayjs(birthday, YYYYMMDD, true);

  // 日付をパースできない.
  if (!isValidDate(base) || !isValidDate(birth)) {
    return null;
  }

  let ibase = Number( base.format(YYYYMMDD) );
  let ibirth = Number( birth.format(YYYYMMDD) );

  return Math.floor( (ibase - ibirth)/10000 );
};

日付計算(年)

 日付計算において、年・月・日の加減算はadd()の引数に"year"、"month"、"day"を指定することで計算を行う。

/**
 * 日付計算(年)
 * @param {string} baseDate 基準日 YYYYMMDD
 * @param {number} year 加減算する年
 * @returns {string|null} 加減算後の日付 YYYYMMDD
 */
exports.addYears = (baseDate, year) => {
  return isValidDate(
    dayjs(baseDate, YYYYMMDD, true).add(year, "year").format(YYYYMMDD)
  );
};

日付計算(月)

/**
 * 日付計算(月)
 * @param {string} baseDate 基準日 YYYYMMDD
 * @param {number} month 加減算する月
 * @returns {string|null} 加減算後の日付 YYYYMMDD
 */
exports.addMonths = (baseDate, month) => {
  return isValidDate(
    dayjs(baseDate, YYYYMMDD, true).add(month, "month").format(YYYYMMDD)
  );
};

日付計算(日)

/**
 * 日付計算(日)
 * @param {string} baseDate 基準日 YYYYMMDD
 * @param {number} day 加減算する日
 * @returns {string|null} 加減算後の日付 YYYYMMDD
 */
exports.addDays = (baseDate, day) => {
  return isValidDate(
    dayjs(baseDate, YYYYMMDD, true).add(day, "day").format(YYYYMMDD)
  );
};

日付フォーマッター

/**
 * 日付フォーマット
 * @param {string} baseDate 日付日時
 * @param {string} format フォーマット. フォーマット形式はDay.js参照.
 * @returns {string|null} フォーマット後日付
 */
exports.format = (baseDate, format) => {
  return isValidDate(dayjs(baseDate).format(format));
};

月末算出

 ライブラリが優秀なので、うるう年も対応しています。


/**
 * 月末の日付を返す.
 * @param {string} baseDate 日付 YYYYMMDD or YYYYMM
 * @returns {string|null} 月末日付 YYYYMMDD
 */
exports.endOfMonth = (baseDate) => {
  return isValidDate(
    dayjs(baseDate, [YYYYMMDD, "YYYYMM"], true).endOf("month").format(YYYYMMDD)
  );
};

日付比較

 isBefore()は基準日と比較して比較対象日付が未来ならばtrue、過去ならばfalseとなります。つまり、基準日.isBefore(過去日 "day") はtrue、基準日.isBefore(未来日, "day")はfalseです。

/**
 * 日付比較
 * @param {string} baseDate 基準日 YYYYMMDD
 * @param {string} compDate 対象日 YYYYMMDD
 * @returns {number|null} 比較結果. 基準日 < 対象日 は -1, 基準日 = 対象日 は 0, 基準日 > 対象日 は 1
 */
exports.compare = (baseDate, compDate) => {
  const base = dayjs(baseDate, YYYYMMDD, true);
  const comp = dayjs(compDate, YYYYMMDD, true);

  if (!isValidDate(base) || !isValidDate(comp)) {
    return null;
  }

  if (base.isSame(comp, "days")) {
    return 0;
  }

  return base.isBefore(comp, "days") ? -1 : 1;
};

日付差分日数

 やっていることは年齢計算と同じで、年を計算するか日数を計算するかの違いです。

/**
 * 差分日数
 * @param {string} fromDate 基準日 YYYYMMDD
 * @param {string} toDate 対象日 YYYYMMDD
 * @returns {number|null} 計算結果. 基準日 < 対象日は 正の値、基準日 > 対象日は 負の値.
 */
exports.diffDays = (fromDate, toDate) => {
  let from = dayjs(fromDate, YYYYMMDD, true);
  let to = dayjs(toDate, YYYYMMDD, true);

  if (!isValidDate(from) || !isValidDate(to)) {
    return null;
  }

  return to.diff(from, "day");
};

エポック <-> UTC時間変換

 UTC時間を使う場合、プラグインを追加します。またエポック時間には秒とミリ秒で使い方が違うので注意します。

エポック秒 to UTC時間

/**
 * エポック(秒) to UTC変換
 * @param {number} seconds Unixエポック
 * @returns {string|null} UTC時間 YYYYMMDD hhmmss
 */
exports.epocSecToUtc = (seconds) => {
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  return isValidDate(dayjs.unix(seconds).utc().format(YYYYMMDD_HHmmss));
};

エポックミリ秒 to UTC変換

/**
 * エポック(ミリ秒) to UTC変換
 * @param {number} miliseconds
 * @returns {string|null} UTC時間 YYYYMMDD hhmmss.SSS
 */
exports.epocMilliSecToUtc = (miliseconds) => {
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  return isValidDate(dayjs(miliseconds).utc().format(YYYYMMDD_HHmmss_SSS));
};

UTC to エポック秒

/**
 * UTC to エポック(秒)
 * @param {string} baseDate 日付
 * @returns {number|null} エポック(秒)
 */
exports.utcToEpocSec = (baseDate) => {
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  let utc = dayjs.utc(baseDate);
  if (!isValidDate(utc)) {
    return null;
  }
  return utc.unix();
};

UTC to エポックミリ秒

/**
 * UTC to エポック(ミリ秒)
 * @param {string} baseDate YYYYMMDD HHmmss.SSS
 * @returns {number|null} エポック(ミリ秒)
 */
exports.utcToEpocMilliSec = (baseDate) => {
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  let utc = dayjs.utc(baseDate);
  if (!isValidDate(utc)) {
    return null;
  }
  return utc.valueOf();
};

UTC <-> JST 変換

 Day.jsオブジェクトは実行環境のロケールに合わせてしまうので、クラウドのリージョンなどの環境では注意が必要ですが、今回は環境に依存しないように実装しています。

UTC to JST

/**
 * UTC to JST変換
 * @param {string} baseUtc UTC時間. YYYYMMDD HHmmss
 * @returns {string|null} 日本時間. YYYYMMDD HHmmss
 */
exports.utcToJst = (baseUtc) => {
  dayjs.extend(require("dayjs/plugin/timezone"));  // 本来はファイルの先頭で宣言
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  dayjs.tz.setDefault("Asia/Tokyo");

  let utc = dayjs.utc(baseUtc, YYYYMMDD_HHmmss);
  if (!isValidDate(utc)) {
    return null;
  }

  return utc.tz().format(YYYYMMDD_HHmmss);
};

JST to UTC

/**
 * JST to UTC変換
 * @param {string} baseJst YYYYMMDD HHmmss
 * @returns {string|null} UTC. YYYYMMDD HHmmss
 */
exports.jstToUtc = (baseJst) => {
  dayjs.extend(require("dayjs/plugin/timezone"));  // 本来はファイルの先頭で宣言
  dayjs.extend(require("dayjs/plugin/utc"));  // 本来はファイルの先頭で宣言
  dayjs.tz.setDefault("Asia/Tokyo");
  let jst = dayjs(baseJst, YYYYMMDD_HHmmss, true);
  if (!isValidDate(jst)) {
    return null;
  }

  return jst.utc().format(YYYYMMDD_HHmmss);
};