I need to do a new column as a subtraction from two (year from previous year), every year will change. So, the first value must be: 49.443
I have tried this but it doesn't work.
"[" & Text.From(Date.Year(DateTime.LocalNow())-1) & "]" -
"[" & Text.From(Date.Year(DateTime.LocalNow())-2) & "]"
Thanks
Not sure if you still need this answered, but I think you're almost there.
DateTime.LocalNow is correct, but you can't use "[" and & because that will result a string literal -- whereas what you want is dynamic field access using something like Record.Field function (within row context of each statement).
This is a table containing some mock/dummy data. Last column (differencia anual) is basically the difference between previous year's values and the values from two years ago (which at the time of writing are 2018 and 2017 respectively).
Try something like:
let
mockData = Table.FromColumns({{11..20}, List.Numbers(40, 10, 2)}, type table [2017 = Int64.Type, 2018 = Int64.Type]),
annualDifference =
let
currentYear = Date.Year(DateTime.LocalNow()),
previousYearHeader = Number.ToText(currentYear - 1),
twoYearsAgoHeader = Number.ToText(currentYear - 2),
differenceColumn = Table.AddColumn(mockData, "diferencia anual", each Record.Field(_, previousYearHeader) - Record.Field(_, twoYearsAgoHeader), type number)
in differenceColumn
in
annualDifference
Since you are using DateTime.LocalNow, the columns being subtracted will be determined by your computer's system clock -- which I think is what you wanted.
The DateUtils.WeekOfTheYear function is great but it uses the ISO 8601 standard definition of a week. That is, a week is considered to start on a Monday and end on a Sunday. I need a similar function that determines the week number based on the system setting for the start of the week. Or at least for either a sunday or monday start of week like MySQL's week function. Anyone have such a function?
ISO-8601 includes a great deal more than just the first day of the week in its specifications for these calculations. There are also rules which determine the first week in the year, for example.
It is not clear whether what you are looking for is a function to replicate the ISO-8601 calculation with these rules otherwise intact and solely varying the first day of the week, or a direct equivalent of the WEEK() function of MySQL, or something else only similar (and not fully defined).
Worth noting is that the MySQL WEEK() function accepts a parameter which does not determine an arbitrary day marking the start of the week, rather it indicates whether either of Sunday or Monday is to be used along with changing a number of other rules that determine the calculated result.
By contrast, the system setting for first day of the week on Windows itself can be ANY day of the week that the user wishes - Mon, Tue, Wed, Thu, Fri, Sat or Sun.
The implementation I provide below is a simple calculation (some might call it naive) which simply returns a value 0..53 based on the number of week periods, or part periods, elapsed between a date specified and the start of the year in which that date occurs.
The week in which 1st of Jan occurs for the year containing the specified date is deemed to be week 0.
Therefore if the 1st of Jan occurs on a Sunday and the "start of week" is defined as Monday then:
Sun, 01-Jan = Week 0
Mon, 02-Jan = Week 1
..
Sun, 08-Jan = Week 1
Mon, 09-Jan = Week 2
..
etc
The Implementation
I have split the implementation into two distinct parts.
The first (WeeksSince01Jan) accepts a date and a parameter indicating the day of week to be considered the first day of the week in the calculation.
This parameter takes a TSYSDayOfWeek value - an enum arranged such that the ordinal values for each day correspond to the values used in system locale settings for the days of the week. (The value returned by the RTL DayOfWeek function uses different values, defined in this code as TRTLDayOfWeek).
The second part of the implementation provides a LocaleWeeksSince01Jan, to demonstrate obtaining the locale defined first day of week for the current user. This is then simply passed thru to a call to WeeksSince01Jan.
type
TSYSDayOfWeek = (sdMon, sdTue, sdWed, sdThu, sdFri, sdSat, sdSun);
TRTLDayOfWeek = 1..7; // Sun -> Sat
function WeeksSince01Jan(const aDate: TDateTime;
const aFirstDayOfWeek: TSYSDayOfWeek): Word;
const
LOCALE_DOW : array[TRTLDayOfWeek] of TSYSDayOfWeek = (sdSun, sdMon, sdTue, sdWed, sdThu, sdFri, sdSat);
var
y, m, d: Word;
dayOfYearStart: TSYSDayOfWeek;
dtYearStart: TDateTime;
dtStartOfFirstWeekInYear: TDateTime;
iAdjust: Integer;
begin
// Get the date for the first day of the year and determine which
// day of the week (Mon-Fri) that was
DecodeDate(aDate, y, m, d);
dtYearStart := EncodeDate(y, 1, 1);
dayOfYearStart := LOCALE_DOW[DayOfWeek(dtYearStart)];
// Week calculation is simply the number of 7 day periods
// elapsed since the start of the year to the specified date,
// adjusted to reflect any 'offset' to the specified first day of week.
iAdjust := Ord(dayOfYearStart) - Ord(aFirstDayOfWeek);
result := (((Trunc(aDate) + iAdjust) - Trunc(dtYearStart)) div 7);
end;
function LocaleWeeksSince01Jan(const aDate: TDateTime): Word;
var
localeValue: array[0..1] of Char;
firstDayOfWeek: TSYSDayOfWeek;
begin
// Get the system defined first day of the week
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, localeValue, SizeOf(localeValue));
firstDayOfWeek := TSYSDayOfWeek(Ord(localeValue[0]) - Ord('0'));
result := WeeksSince01Jan(aDate, firstDayOfWeek);
end;
If you have more complex rules to determine the 0th or 1st week of a year based on numbers of days in that week etc, then you will need to modify this implementation accordingly. There is no attempt to accommodate such needs in the current implementation.
For Testing
The code below may be used as the basis for testing the output (using the system defined first day of the week):
const
YEAR = 2012;
var
d: Integer;
dt: TDateTime;
wk: Word;
begin
List.Items.Clear;
dt := EncodeDate(YEAR, 1, 1) - 7;
for d := 1 to 380 do
begin
dt := dt + 1;
wk := LocaleWeeksSince01Jan(dt);
List.Items.Add(Format('%s, %s = week %d', [ShortDayNames[DayOfWeek(dt)],
DateToStr(dt),
wk]));
end;
Where List is a reference to a TListbox.
Change the value of YEAR to produce a range of results that cover all dates in the specified year +/- an additional 7/8 days, to illustrate the change in result at year end of the preceding and succeeding years.
NOTE: 2012 is a year which demonstrates the possibility of returning dates in that year covering the full range of potential results, 0..53.
If you are only interested in week starting on Sunday instead on Monday you can simply substract 1 day from your DateTime value before feeding it to DateUtils.WeekOfTheYear function.
EDIT:
Response to David Heffernan comment:
Imagine what happens when you subtract 1 from January 1st
It depends on which day is on January 1st
From Embarcadero documentation: http://docwiki.embarcadero.com/Libraries/XE8/en/System.DateUtils.WeekOfTheYear
AYear returns the year in which the specified week occurs. Note that
this may not be the same as the year of AValue. This is because the
first week of a year is defined as the first week with four or more
days in that year. This means that, if the first calendar day of the
year is a Friday, Saturday, or Sunday, then for the first three, two,
or one days of the calendar year, WeekOfTheYear returns the last week
of the previous year. Similarly, if the last calendar day of the year
is a Monday, Tuesday, or Wednesday, then for the last one, two, or
three days of the calendar year, WeekOfTheYear returns 1 (the first
week of the next calendar year).
So if the week starts with Sunday instead of Monday then it means that week start and end days are simply shifted by one day backward.
So for such occasions it is best to use over-ridden version with additional variable parameter to which the year that this week belongs to is stored.
I've combined Deltics' great code with SilverWarior's simple idea to create a WeekOfYear function that handles the system week start day.
type
TSYSDayOfWeek = (sdMon, sdTue, sdWed, sdThu, sdFri, sdSat, sdSun);
function LocaleWeekOfTheYear(dte: TDateTime): word;
var
localeValue: array[0..1] of Char;
firstDayOfWeek: TSYSDayOfWeek;
yearOld,yearNew: word;
dteNew: TDateTime;
begin
// Get the system defined first day of the week
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, localeValue, SizeOf(localeValue));
firstDayOfWeek := TSYSDayOfWeek(Ord(localeValue[0]) - Ord('0'));
yearOld:= Year(dte);
if (firstDayOfWeek=sdSun) then
dteNew:= dte-1
else
dteNew:= dte+Ord(firstDayOfWeek);
yearNew:= Year(dteNew);
if (yearOld=yearNew) then
dte:= dteNew;
Result:= WeekOfTheYear(dte);
end;
To make the first day of the week is Saturday, I use to add 2 to the value of Now date.
As example WeekOf(Now + 2) : makes the first day is Saturday,
WeekOf(Now + 2) : makes the first day is Sunday,
WeekOf(Now + 0) : makes the first day is Monday,
I'm trying to convert strings like "Sep 11, Oct 31, Feb 28" into DateTime instances as part of a screen-scraper. Using DateTime.parse() works fine apart from when the data goes across years, and it naively (and probably correctly) returns a date in the current year.
For example the following test case.
test "dateA convert next year" do
TimeService.stubs(:now).returns(Time.new(2013, 12, 30, 9, 30))
assert_equal(Date.new(2014, 1, 2), Extraction.dateA("Jan 2"))
end
I updated my method to look at what would be date with year + 1, and return the closest to 'now' - this works fine. However it feels a bit ugly, and I'm looking for a more elegant solution.
def Extraction.dateA(content)
return_value = DateTime.parse(content)
next_year = return_value.change(:year => return_value.year + 1)
now = TimeService.now.to_i
if (next_year.to_i - now).abs < (return_value.to_i - now).abs then
return_value = next_year
end
return_value
end
TimeService.now is just a utility to return current time to help with stubbing.
Excuse my ruby, I'm new to it.
I think this works as intended, allowing for closest date in previous year as well:
module Extraction
def Extraction.date_a(content)
parsed_date = DateTime.parse(content)
now = DateTime.now
dates = [ parsed_date, parsed_date.next_year, parsed_date.prev_year ]
dates.min_by { |d| ( d - now ).abs }
end
end
A few points:
Changed method name to date_a, just a Ruby convention that differs from Java.
I made use of some built-in methods next_year and prev_year on DateTime
I used a time difference metric and selected date with the minimal value of it from three candidate dates (this is what min_by does). This is simpler code than the conditional switching, especially with three dates to consider.
I forgot about min_by originally, I don't use it often, but it's a very good fit for this problem.
Note there is a pathological case - "29 Feb". If it appears correctly in text, by its nature it will define which year is valid, and it won't parse if current year is e.g. 2015.
I need to loop through all of the days and months for the past couple decades numerically as well as to have the name of the month and day for each date. Obviously a few series of loops can accomplish this, but I wanted to know the most concise ruby-like way to accomplish this.
Essentially I'd need output like this for each day over the past X years:
3 January 2011 and 1/3/2011
What's the cleanest approach?
Dates can work as a range, so it's fairly easy to iterate over a range. The only real trick is how to output them as a formatted string, which can be found in the Date#strftime method, which is documented here.
from_date = Date.new(2011, 1, 1)
to_date = Date.new(2011, 1, 10)
(from_date..to_date).each { |d| puts d.strftime("%-d %B %Y and %-m/%-d/%Y") }
# => 1 January 2011 and 1/1/2011
# => 2 January 2011 and 1/2/2011
# => ...
# => 9 January 2011 and 1/9/2011
# => 10 January 2011 and 1/10/2011
(Note: I recall having some bad luck a ways back with unpadded percent formats like %-d in Windows, but if the above doesn't work and you want them unpadded in that environment you can remove the dash and employ your own workarounds.)
Given start_date & end_date:
(start_date..end_date).each do |date|
# do things with date
end
as David said, this is possible because of Date#succ. You can use Date#strftime to get the date in any format you'd like.
See if you can construct a Range where the min and max are Date objects, then call .each on the range. If the Date object supports the succ method this should work.
I have 2 independent but contiguous date ranges. The first range is the start and end date for a project. Lets say start = 3/21/10 and end = 5/16/10. The second range is a month boundary (say 3/1/10 to 3/31/10, 4/1/10 to 4/30/10, etc.) I need to figure out how many days in each month fall into the first range.
The answer to my example above is March = 10, April = 30, May = 16.
I am trying to figure out an excel formula or VBA function that will give me this value.
Any thoughts on an algorithm for this? I feel it should be rather easy but I can't seem to figure it out.
I have a formula which will return TRUE/FALSE if ANY part of the month range is within the project start/end but not the number of days. That function is below.
return month_start <= project_end And month_end >= project_start
Think it figured it out.
=MAX( MIN(project_end, month_end) - MAX(project_start,month_start) + 1 , 0 )