How to convert year month and min information to day num - matlab

I want to convert the given year, month and min information to day of year info.
For eg lets say
year 2004, month 2, day 2 = 33rd day of year
how can I do it in matlab?

Get the datenum for Jan 1 of that year, and subtract it from the given yy/mm/dd. For example, today's day of the year:
jan1 = datenum(datestr(now,'yy'),'yy')
now - jan1 + 1
Check the above against here.
For a specific date,
>> yy = 2004; mm = 2; dd = 2;
>> doty = datenum(yy,mm,dd) - datenum(yy,1,0)
doty =
33

Related

Assessing which person is the one with the next birthday

In Stata I am trying to assess which of the given birthdays is the next one compared with a given date. My data looks like this:
All dates are in daily format (%dD_m_Y), e.g. 18mar1926
Variable date which is the reference date with which all other dates should be compared
Variables birth1, birth2, birth3, birth4, birth5, birth6 contain the birthday of all possible household members.
For example: A household with two adults A and B. The birthday of A is 20th Nov 1977 and the birthday of person B is 30th March 1978. The reference date is 29.11.2020. I want to know who is the person who has the next birthday, in the example above it is person B, because person A has had its birthday one week before the reference date, so the next birthday in this household will be celebrated on the 30 March 2021.
Example data:
date
birth1
birth2
birth3
birth4
birth5
birth6
02feb2021
15jan1974
27nov1985
30nov2020
31aug1945
27jun1999
07apr1997
19nov2020
27sep1993
30dec1996
29jan2021
29mar1973
05dec2020
21jan1976
02oct1976
21jan1976
25may1995
15feb1997
25nov2020
25nov1943
29nov1946
02feb2021
28apr1979
EDITED to account for Feb 29
*The edit will treat people who have a February 29 birthday as if it were March 1 in cases when the year of date is not a leap year. If that doesn't make sense for your particular use case, it should be easy to alter the code below as you see fit.
Since you want the next birthday in the year rather than the closest birthday, you can use the year of date and the month and day from birth{i} to create a date for each person's next birthday. Then you can sinmply take the earliest value from each household. I reshape long, and generate a person and household id in order to do this.
Make example data
clear
set obs 6
set seed 1996
generate date = floor((mdy(12,31,2020)-mdy(12,1,2015)+1)*runiform() + mdy(12,1,2015))
format date %td
forvalue i = 1/6 {
gen birth`i' = floor((mdy(12,31,1996)-mdy(12,1,1980)+1)*runiform() + mdy(12,1,1980)) if _n < `i' == 0
format birth`i' %td
}
replace birth6 = birth4 in 6 // want a tie
replace birth2 = date("29feb1996","DMY") in 3 // Feb 29
Find Next Birthday
gen household_id = _n
reshape long birth, i(date household_id) j(person)
drop if mi(birth)
gen person_next_birthday = mdy( month(birth), day(birth), year(date))
* TREAT FEB 29 as if they have a march 1 birthday in non-leap years
replace person_next_birthday = mdy(3,1,year(date)) if month(birth) == 2 ///
& day(birth) == 29 & mod(year(date),4)!=0
replace person_next_birthday = mdy( month(birth), day(birth), year(date) + 1) if person_next_birthday < date
replace person_next_birthday = mdy(3,1,year(date)+1) if month(birth) == 2 ///
& day(birth) == 29 & mod(year(date) + 1,4)!=0 & person_next_birthday < date
format person_next_birthday %td
bysort household_id (person_next_birthday): gen next_bday = person_next_birthday[1]
format next_bday %td
drop person_next_birthday
reshape wide birth, i(date household_id next_bday) j(person)
gen next_bday_persons = ""
* Make a string to present household persons who have next bday
foreach v of varlist birth* {
local person = subinstr("`v'","birth","",.)
local condition = "month(`v') == month(next_bday) & day(`v') == day(next_bday)"
local condition_feb29 = "month(next_bday) == 3 & day(next_bday) == 1 & month(`v') == 2 & day(`v') == 29"
replace next_bday_persons = next_bday_persons + "|`person'" if `condition' | `condition_feb29'
}
replace next_bday_persons = regexr(next_bday_persons,"^\|","")
order next_bday_persons, after(next_bday)
The last loop is unnecessary, but illustrates that this is robust to ties.

Serial date number since "Jan 1, 0000": is this -1 BC or +1 CE?

I was working on dates in Matlab and Octave, and the "serial date number" format is documented as
A single number equal to the number of days since January 0, 0000 in the proleptic ISO calendar (specifying use of the Gregorian calendar).
In Octave, they document
Return the date/time input as a serial day number, with Jan 1, 0000 defined as day 1.
The gregorian calender does not use a year zero. But Matlab and Octave refer to the year zero. Does this mean that they refer to year -1 BC, as in the astronomical year numbering?
Days before "October 15, 1582" are "wrong by as much as eleven days", according to the octave manual, which is considerably smaller than a complete year. So I'm trying to sort out this ambiguity.
Firstly, note that the MATLAB and Octave definitions are equivalent
[MATLAB] N = "number of days since Jan 0, 0000" ⇔ [OCTAVE] "Jan 1, 0000 is day 1"
Since N = 1 on day 1.
The Wikipedia page on "Year Zero" (which you linked to) offers this:
[...] the year 1 BC is followed by AD 1. However, there is a year zero in astronomical year numbering (where it coincides with the Julian year 1 BC) and in ISO 8601:2004 (where it coincides with the Gregorian year 1 BC), as well as in all Buddhist and Hindu calendars.
MATLAB and Octave appear to have followed the ISO standard, as stated in the datetime docs:
datetime arrays represent points in time using the proleptic ISO calendar
So the year zero, and hence the datenum value of 1 days, coincides with the first day of 1BC.
Per the definitions at the top of this answer
"day 1"
= 1/Jan/0000
= datenum(1)
= datetime( 1, 'ConvertFrom', 'datenum' )
= datetime( 0, 0, 1 )
We can test using datenum (number of days) and datetime (datetime type object)
datenum( 0, 0, 1 ) % = 1, as defined by the docs
datetime( 1, 'ConvertFrom', 'datenum' )
% = 1/Jan/0000 00:00:00
datetime( 1 + 366, 'ConvertFrom', 'datenum' )
% = 1/Jan/0001 00:00:00
% First day of year 1 after 366 days (leap year 0000 + 1 for Jan 1 )

Convert string month to numeric

I have a variable period that contains a month as an abbreviated string (i.e. "JAN", "FEB", "MAR", etc). How do I convert period to a numeral (i.e. 1, 2, 3, etc)?
My solution is:
gen fake_date_s = "2000"+period+"1"
gen fake_date = date(fake_date_s, "YMD")
gen month = month(fake_date)
I don't think it's ugly:
clear
input ///
str3 period
JAN
FEB
DEC
end
list
gen monthnum = month(date("2000" + period + "1", "YMD"))
list
This also works:
gen monthnum = month(date(period, "M"))
as it sets the day and the year in the daily date to 01 and 1960, by default.
I'm sure you can find an alternative that doesn't use date functions, but why not use them?
Another approach is:
local i=1
foreach m in `c(Mons)' {
replace month = "`i'" if month == upper("`m'")
local ++i
}
destring month, replace

Compare dates in Lua

I have a variable with a date table that looks like this
* table:
[day]
* number: 15
[year]
* number: 2015
[month]
* number: 2
How do I get the days between the current date and the date above? Many thanks!
You can use os.time() to convert your table to seconds and get the current time and then use os.difftime() to compute the difference. see Lua Wiki for more details.
reference = os.time{day=15, year=2015, month=2}
daysfrom = os.difftime(os.time(), reference) / (24 * 60 * 60) -- seconds in a day
wholedays = math.floor(daysfrom)
print(wholedays) -- today it prints "1"
as #barnes53 pointed out could be off by one day for a few seconds so it's not ideal, but it may be good enough for your needs.
You can use the algorithms gathered here:
chrono-Compatible Low-Level Date Algorithms
The algorithms are shown using C++, but they can be easily implemented in Lua if you like, or you can implement them in C or C++ and then just provide Lua bindings.
The basic idea using these algorithms is to compute a day number for the two dates and then just subtract them to give you the number of days.
--[[
http://howardhinnant.github.io/date_algorithms.html
Returns number of days since civil 1970-01-01. Negative values indicate
days prior to 1970-01-01.
Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
m is in [1, 12]
d is in [1, last_day_of_month(y, m)]
y is "approximately" in
[numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
Exact range of validity is:
[civil_from_days(numeric_limits<Int>::min()),
civil_from_days(numeric_limits<Int>::max()-719468)]
]]
function days_from_civil(y, m, d)
if m <= 2 then
y = y - 1
m = m + 9
else
m = m - 3
end
local era = math.floor(y/400)
local yoe = y - era * 400 -- [0, 399]
local doy = math.modf((153*m + 2)/5) + d-1 -- [0, 365]
local doe = yoe * 365 + math.modf(yoe/4) - math.modf(yoe/100) + doy -- [0, 146096]
return era * 146097 + doe - 719468
end
local reference_date = {year=2001, month = 1, day = 1}
local date = os.date("*t")
local reference_days = days_from_civil(reference_date.year, reference_date.month, reference_date.day)
local days = days_from_civil(date.year, date.month, date.day)
print(string.format("Today is %d days into the 21st century.",days-reference_days))
os.time (under Windows, at least) is limited to years from 1970 and up. If, for example, you need a general solution to also find ages in days for people born before 1970, this won't work. You can use a julian date conversion and subtract between the two numbers (today and your target date).
A sample julian date function that will work for practically any date AD is given below (Lua v5.3 because of // but you could adapt to earlier versions):
local
function div(n,d)
local a, b = 1, 1
if n < 0 then a = -1 end
if d < 0 then b = -1 end
return a * b * (math.abs(n) // math.abs(d))
end
--------------------------------------------------------------------------------
-- Convert a YYMMDD date to Julian since 1/1/1900 (negative answer possible)
--------------------------------------------------------------------------------
function julian(year, month, day)
local temp
if (year < 0) or (month < 1) or (month > 12)
or (day < 1) or (day > 31) then
return
end
temp = div(month - 14, 12)
return (
day - 32075 +
div(1461 * (year + 4800 + temp), 4) +
div(367 * (month - 2 - temp * 12), 12) -
div(3 * div(year + 4900 + temp, 100), 4)
) - 2415021
end

Get today -2 (skipping weekend)

How can I get the Today -2 days (the last 2 working days from now)? but skipping the weekend?
Example #1: Today is February 25, I want February 21
Example #2: Today is February 26, I want February 24
PS: Date format is DD/MM/YYYY
I have this, but the result is going forward, should I use datediff or what?:
<%
Dim d
d = DateAdd("m", 1, Now)
d = "01/" & Month(d) & "/" & Year(d)
d = DateAdd("d", -1, d)
If Weekday(d) = 7 Then
d = DateAdd("d", -1, d)
ElseIf Weekday(d) = 1 Then
d = DateAdd("d", -2, d)
End If
Response.Write "Day: " & d
%>
To get your desired result you need to subtract 3 days on Saturdays, 4 days on Sundays and Mondays, and 2 days on all other days. This can be achieved with something like this:
today = Now
num = Weekday(today, vbWednesday)
d = today - (2 + num\5 + num\6)
response.write "Two working days back: " & d
The Weekday function returns a numeric value for each weekday. By basing the week on Wednesday you can calculate the additional number of days you need to subtract from the current date with integer divisions:
num\5 returns 1 for Saturday, Sunday and Monday, and 0 otherwise.
num\6 returns 1 for Sunday and Monday, and 0 otherwise.
Thus the term 2 + num\5 + num\6 becomes 3 for Saturdays, 4 for Sundays and Mondays, and 2 for all other days.
This might be overkill for what you need but here are two routines I use in my scripts to add or subtract workdays while considering weekends and holidays.
Function AddWorkingDays(dtStart, intDays)
' Start/Default case...
AddWorkingDays = CDate(dtStart)
' If positive days, step forward, otherwise step backward...
Dim intStep, intCount
If intDays > 0 Then intStep = 1 Else intStep = -1
Do While intCount <> intDays
AddWorkingDays = AddWorkingDays + intStep
If IsValidDate(AddWorkingDays) Then intCount = intCount + intStep
Loop
End Function
Function IsValidDate(d)
Dim intWeekday, intMonth, intDay
intWeekday = Weekday(d)
intMonth = Month(d)
intDay = Day(d)
' Weekend dates are not acceptable...
If intWeekday = vbSaturday Or intWeekday = vbSunday Then Exit Function
' Holidays are also not acceptable...
If intMonth = 01 Then If intDay = 01 Then Exit Function ' New Year's Day
If intMonth = 07 Then If intDay = 04 Then Exit Function ' Independence Day
If intMonth = 12 Then If intDay = 25 Then Exit Function ' Christmas Day
' Memorial Day is the last Monday in May...
If intWeekday = vbMonday Then If intMonth = 05 Then If intDay >= 25 Then Exit Function
' ... (Thanksgiving, others) ...
' All tests passed. Date is a valid workday...
IsValidDate = True
End Function