1%%% Copyright (C) 2009 Enrique Marcote, Miguel Rodriguez
2%%% All rights reserved.
3%%%
4%%% Redistribution and use in source and binary forms, with or without
5%%% modification, are permitted provided that the following conditions are met:
6%%%
7%%% o Redistributions of source code must retain the above copyright notice,
8%%%   this list of conditions and the following disclaimer.
9%%%
10%%% o Redistributions in binary form must reproduce the above copyright notice,
11%%%   this list of conditions and the following disclaimer in the documentation
12%%%   and/or other materials provided with the distribution.
13%%%
14%%% o Neither the name of ERLANG TRAINING AND CONSULTING nor the names of its
15%%%   contributors may be used to endorse or promote products derived from this
16%%%   software without specific prior written permission.
17%%%
18%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19%%% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20%%% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21%%% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22%%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23%%% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24%%% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25%%% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26%%% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27%%% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28%%% POSSIBILITY OF SUCH DAMAGE.
29-module(cl_calendar).
30
31%%% EXTERNAL EXPORTS
32-export([day/0,
33         day/1,
34         day/3,
35         day_of_current_week_to_date/1,
36         day_of_next_week_to_date/1,
37         format_day/1,
38         format_month/1,
39         format_rfc2109/1,
40         format_rfc2109/2,
41         local_time/0,
42         parse/1,
43         time_difference/2,
44         tstamp/0,
45         tstamp/1,
46         tstamp_to_local_time/1,
47         valid_time/1,
48         valid_time/3,
49         week/0,
50         week/1,
51         week/3]).
52
53%%% MACROS
54-define(JANUARY_1ST_1970, 62167219200).
55-define(SEPARATORS, [$/, $-, $_, $:, $ ]).
56
57%%%-----------------------------------------------------------------------------
58%%% EXTERNAL EXPORTS
59%%%-----------------------------------------------------------------------------
60day() ->
61    day(date()).
62
63day({Year, Month, Day}) ->
64    day(Year, Month, Day).
65
66day(Year, Month, Day) ->
67    January1st = calendar:date_to_gregorian_days(Year, 1, 1),
68    calendar:date_to_gregorian_days(Year, Month, Day) - January1st + 1.
69
70
71day_of_current_week_to_date(DayNumber) ->
72    CurrentDayNumber = calendar:date_to_gregorian_days(date()),
73    CurrentDayOfWeek = calendar:day_of_the_week(date()),
74    calendar:gregorian_days_to_date(
75      CurrentDayNumber + DayNumber - CurrentDayOfWeek).
76
77
78day_of_next_week_to_date(DayNumber) ->
79    CurrentDayNumber = calendar:date_to_gregorian_days(date()),
80    CurrentDayOfWeek = calendar:day_of_the_week(date()),
81    calendar:gregorian_days_to_date(
82      CurrentDayNumber + DayNumber + 7 - CurrentDayOfWeek).
83
84
85format_day(1) ->
86    "Mon";
87format_day(2) ->
88    "Tue";
89format_day(3) ->
90    "Wed";
91format_day(4) ->
92    "Thu";
93format_day(5) ->
94    "Fri";
95format_day(6) ->
96    "Sat";
97format_day(7) ->
98    "Sun".
99
100
101format_month(1) ->
102    "Jan";
103format_month(2) ->
104    "Feb";
105format_month(3) ->
106    "Mar";
107format_month(4) ->
108    "Apr";
109format_month(5) ->
110    "May";
111format_month(6) ->
112    "Jun";
113format_month(7) ->
114    "Jul";
115format_month(8) ->
116    "Aug";
117format_month(9) ->
118    "Sep";
119format_month(10) ->
120    "Oct";
121format_month(11) ->
122    "Nov";
123format_month(12) ->
124    "Dec".
125
126
127format_rfc2109(DateTime) ->
128    format_rfc2109(DateTime, "GMT").
129
130format_rfc2109({{Year, Month, Day}, {Hour, Min, Sec}}, Timezone) ->
131    % Estrange but much faster than using io_lib
132    FDay = format_day(calendar:day_of_the_week({Year, Month, Day})),
133    FMonth = format_month(Month),
134    Time = [pad(Hour), ":", pad(Min), ":", pad(Sec), " ", Timezone],
135    lists:concat([FDay, ", ", pad(Day), "-", FMonth, "-", Year, " " | Time]).
136
137
138local_time() ->
139    % Estrange but much faster than using io_lib
140    {{Year, Month, Day}, {Hour, Min, Sec}} = erlang:localtime(),
141    LocalTime = Sec + (Min * 100) + (Hour * 10000) + (Day * 1000000) +
142        (Month * 100000000) + (Year * 10000000000),
143    integer_to_list(LocalTime).
144
145
146parse([H1, H2, _, M1, M2, _, S1, S2]) ->
147    Hour = list_to_integer([H1, H2]),
148    Min = list_to_integer([M1, M2]),
149    Sec = list_to_integer([S1, S2]),
150    true = valid_time(Hour, Min, Sec),
151    {Hour, Min, Sec};
152parse([Y1, Y2, Y3, Y4, _, M1, M2, _, D1, D2]) ->
153    Year = list_to_integer([Y1, Y2, Y3, Y4]),
154    Month = list_to_integer([M1, M2]),
155    Day = list_to_integer([D1, D2]),
156    true = calendar:valid_date(Year, Month, Day),
157    {Year, Month, Day};
158parse([Y1, Y2, Y3, Y4, S1, M1, M2, S2, D1, D2, _ | Time]) ->
159    {parse([Y1, Y2, Y3, Y4, S1, M1, M2, S2, D1, D2]), parse(Time)}.
160
161
162time_difference(Time1, Time2) ->
163    calendar:time_to_seconds(Time1) - calendar:time_to_seconds(Time2).
164
165
166tstamp() ->
167    tstamp(erlang:localtime()).
168
169tstamp(Time) ->
170    calendar:datetime_to_gregorian_seconds(Time) - ?JANUARY_1ST_1970.
171
172
173tstamp_to_local_time(Tstamp) ->
174    calendar:gregorian_seconds_to_datetime(Tstamp + ?JANUARY_1ST_1970).
175
176
177valid_time({H, M, S}) ->
178    valid_time(H, M, S).
179
180valid_time(H, M, S) when H >= 0, H < 24, M >= 0, M < 60, S >= 0, S < 60 ->
181    true;
182valid_time(_, _, _) ->
183    false.
184
185
186week() ->
187    week(date()).
188
189week({Year, Month, Day}) ->
190    week(Year, Month, Day).
191
192week(Year, Month, Day) ->
193    DaysUntil1stSunday = 7 - calendar:day_of_the_week(Year, 1, 1) + 1,
194    DaysSince1stSunday = day(Year, Month, Day) - DaysUntil1stSunday,
195    if
196        (DaysSince1stSunday rem 7) > 0 ->
197            (DaysSince1stSunday div 7) + 1;
198        true ->
199            (DaysSince1stSunday div 7)
200    end.
201
202%%%-----------------------------------------------------------------------------
203%%% INTERNAL FUNCTIONS
204%%%-----------------------------------------------------------------------------
205pad(N) when N > 9 ->
206    N;
207pad(N) ->
208    [$0 | integer_to_list(N)].
209