Java 分析if语句中的日期并仅显示规定时间范围内的结果

Java 分析if语句中的日期并仅显示规定时间范围内的结果,java,simpledateformat,Java,Simpledateformat,目前,我正在编写一个java程序,以确定a或其他文件是在哪一天形成的 用户在执行程序时必须输入2个日期作为参数,例如java ReadingTest 2016-09-03 2016-10-31,程序将查找2016-09-03至2016-10-31之间的锤子模式 代码如下: import java.io.*; import java.util.*; import java.text.*; public class ReadingTest { public static void main

目前,我正在编写一个java程序,以确定a或其他文件是在哪一天形成的

用户在执行程序时必须输入2个日期作为参数,例如java ReadingTest 2016-09-03 2016-10-31,程序将查找2016-09-03至2016-10-31之间的锤子模式

代码如下:

import java.io.*;
import java.util.*;
import java.text.*;

public class ReadingTest
{
    public static void main(String[] args) throws IOException,ParseException
    {
    //Import file to java
    File file = new file("table.csv");

    //Read the file
    Scanner infile = new Scanner(file);

    //Skip the first line in table.csv
    infile.nextLine();

    //Define format of date
    SImpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    //Name the variables user enters
    Date start = sdf.parse(args[0]);
    Date end = sdf.parse(args[1]);

    //Create ArrayList for each column of data
    ArrayList<String> date = new ArrayList<String>();
    ArrayList<Double> open = new ArrayList<Double>();
    ArrayList<Double> high = new ArrayList<Double>();
    ArrayList<Double> low = new ArrayList<Double>();
    ArrayList<Double> close = new ArrayList<Double>();

    while (infile.hasNext())
    {
        //Tokenize columns by comma
        String[] data = infile.nextLine().split(",");
        //Organize each column of data to one index of data array
        date.add(data[0]);
        open.add(Double.parseDouble(data[1]));
        high.add(Double.parseDouble(data[2]));
        low.add(Double.parseDouble(data[3]));
        close.add(Double.parseDouble(data[4]));
    }
    //Show options and ask user to choose
    System.out.println("1. Hammer");
    System.out.println("2. Three white soldiers");
    System.out.println("3. Bullish kicker");

    //Record user input and execute corresponding code
    Scanner input = new Scanner(System.in);
    int choice = input.nextInt();

    if (choice == 1)
        for (int i = 0; i < date.size(); i++)
            if (close.get(i) > open.get(i) &&
                close.get(i) > ((high.get(i)) + (low.get(i)))/2 &&
                ((close.get(i) - low.get(i))/2 > (high.get(i) - close.get(i)))
            System.out.println("Pattern found: " + date.get(i));
}
}
我还尝试仅显示显示日期,使用:

并导致

error: cannot find symbol
symbol: method before(Date)
location: class String
CSV文件如下所示:

Date       Open  High  Low  Close  
31/10/2016 58.25 58.65 58.2 58.35
28/10/2016 58.95 59    58.3 58.35
.
.
.
1/8/2016   50.8  51.1 50.75  50.8

应如何修改代码以使其正常工作?

文件中的格式不是“yyyy-MM-dd”,而是“dd/MM/yyyy”,因此无法使用变量sdf对其进行解析。您可以定义第二种日期格式

SImpleDateFormat sdfParse = new SimpleDateFormat("dd/MM/yyyy");
然后使用此文件进行解析,并使用您的文件进行格式化:

sdf.format(sdfParse.parse(date.get(i)))

希望得到更好的结果。

我想你想要的是这个

SimpleDateFormat hammerFormat = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat slashFormat = new SimpleDateFormat("dd/MM/yyyy");
因此,您可以将日期解析为
yyyy-MM-dd
表示形式,如下所示

hammerFormat.format(slashFormat.parse(date.get(i))));
完整代码

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Scanner;

public class ReadingTest {
    public static void main(String[] args) throws IOException, ParseException {
        // Import file to java
        File file = new File("table.csv");

        // Read the file
        Scanner infile = new Scanner(file);

        // Skip the first line in table.csv
        infile.nextLine();

        // Define format of date
        SimpleDateFormat hammerFormat = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat slashFormat = new SimpleDateFormat("dd/MM/yyyy");

        // Name the variables user enters
        Date start = hammerFormat.parse(args[0]);
        Date end = hammerFormat.parse(args[1]);

        // Create ArrayList for each column of data
        ArrayList < String > date = new ArrayList < String > ();
        ArrayList < Double > open = new ArrayList < Double > ();
        ArrayList < Double > high = new ArrayList < Double > ();
        ArrayList < Double > low = new ArrayList < Double > ();
        ArrayList < Double > close = new ArrayList < Double > ();

        while (infile.hasNext()) {
            // Tokenize columns by comma
            String[] data = infile.nextLine().split(",");
            // Organize each column of data to one index of data array
            date.add(data[0]);
            open.add(Double.parseDouble(data[1]));
            high.add(Double.parseDouble(data[2]));
            low.add(Double.parseDouble(data[3]));
            close.add(Double.parseDouble(data[4]));
        }
        // Show options and ask user to choose
        System.out.println("1. Hammer");
        System.out.println("2. Three white soldiers");
        System.out.println("3. Bullish kicker");

        // Record user input and execute corresponding code
        Scanner input = new Scanner(System.in);
        int choice = input.nextInt();

        if (choice == 1)
            for (int i = 0; i < date.size(); i++)
                if (close.get(i) > open.get(i) && close.get(i) > ((high.get(i)) + (low.get(i))) / 2 && ((close.get(i) - low.get(i)) / 2 > (high.get(i) - close.get(i))))
                    System.out.println("Pattern found: " + hammerFormat.format(slashFormat.parse(date.get(i))));
    }
}
这对我来说很好。执行程序时,我传递了两个参数
2016-09-03 2016-10-31

tl;博士 解析日期

对于日期时间工作,仅使用java.time类

LocalDate start = LocalDate.parse( "2018-10-27" );  // Parse input string as a date-only value (no time-of-day, no time zone), a `java.time.LocalDate` object.
…在if语句中,仅显示规定时间范围内的结果

更好的是,使用ThreeTen Extra library中的
LocalDateRange
类来表示开始-结束日期范围

细节 应如何修改该准则以使其发挥作用

你可能会后悔你的要求。这个回答让我有点发疯,我对这个问题很好奇,并且想和更多的人一起工作

确定日期和时间处理 您使用的是麻烦的旧日期时间类,这些类在几年前就被取代了,但是java.time类

此外,您正在滥用这些遗留类。您正在将仅日期值放入具有日期时间类型的日期中。改为使用
java.time.LocalDate
作为仅限日期的值

无法定义与输入匹配的格式模式。您的模式显示为“yyyy-MM-dd”,但您的输入是按日-月-年顺序,而不是按年-月-日顺序。定义与字符串输入匹配的格式模式。使用现代类,
DateTimeFormatter

DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" ) ;
解析输入字符串

LocalDate ld = LocalDate.parse( "31/10/2016" , f ) ;
ld.toString():2018-10-31

请清楚,日期时间对象没有“格式”。格式与表示日期时间值/对象的文本、从日期时间对象生成的
字符串
对象相关。但是日期时间对象和
字符串仍然是分离的

顺便说一下,不要对输入使用这种自定义或本地化格式在将日期时间值交换为文本时,始终使用标准格式。默认情况下,java.time类在解析/生成字符串时使用这些标准格式

为数据模型定义一个类 与其乱弄数组或一堆
列表
对象,不如为数据定义一个类。将其命名为
StockDay
,以表示股票在特定日期的表现

切勿使用
double
double
float
表示货币。这些类型使用的是一种折衷精度的方法,以获得更快的执行性能。而是使用类;速度较慢,但准确

在这个类中,我们在定义。为了简单起见,我使用了更严格的定义,不允许使用上面的“阴影”。所有这些代码仅供演示,完全未经测试:使用风险自负

package com.basilbourque.example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Objects;

public class StockDay implements Comparable < StockDay > {

    // Example data:
    // Date       Open  High  Low  Close
    // 31/10/2016 58.25 58.65 58.2 58.35

    // ---------|  Members  |------------------------
    private LocalDate date;
    private BigDecimal open, high, low, close;
    private Boolean isHammer;

    // ---------|  Constructors  |------------------------
    public StockDay ( final LocalDate localDateArg , final BigDecimal openArg , final BigDecimal highArg , final BigDecimal lowArg , final BigDecimal closeArg ) {
        // In real work, add defensive code to validate data such as no nulls, only positive numbers, reasonable dates.
        this.date = localDateArg;
        this.open = openArg;
        this.high = highArg;
        this.low = lowArg;
        // Verify the high is greater than or equal to the low.
        if ( this.high.compareTo( this.low ) < 0 ) {
            throw new IllegalArgumentException( "The passed High is below the passed Low for Date of " + this.date + ". Not possible." );
        }
        this.close = closeArg;
        this.isHammer = this.determineHammer();
    }

    private Boolean determineHammer () {
        // A hammer is a price pattern in candlestick charting that occurs when a security trades significantly lower than its opening, but rallies later in the day to close either above or near its opening price. This pattern forms a hammer-shaped candlestick, in which the body is at least half the size of the tail or wick.
        // Read more: Hammer https://www.investopedia.com/terms/h/hammer.asp#ixzz5G6rqtbkv
        // See also: http://www.onlinetradingconcepts.com/TechnicalAnalysis/Candlesticks/Hammer.html

        // Caveat: This code is a quick rough draft, not thought-through, and totally untested. Use at your own risk. For demonstration purposes only.

        // First check if the High is above the Close. A Hammer has little or no upper  "shadow" (line protruding above the box). We'll go with "no shadow" for simplicity here.
        if ( this.high.compareTo( this.close ) > 0 ) { // if high > close, not a hammer.
            return Boolean.FALSE;
        }

        // Proceed with next check: Is "tail" (lower shadow) at least twice as long as height of box.
        BigDecimal closeOpenDeltaAbsolute_BoxHeight = this.close.subtract( this.open ).abs();
        BigDecimal lowerOfCloseOrOpen = ( this.close.compareTo( this.open ) < 0 ) ? this.close : this.open;  // If x is smaller than y, use x. If x is greater than or equal to y, use y.
        BigDecimal lowerShadowHeight = lowerOfCloseOrOpen.subtract( this.low );
        // A Hammer has a long lower shadow (delta between either Close or Open, whichever is lower, and the Low), at least twice as tall as the box (Close-Open absolute delta).
        BigDecimal requiredMinimumLengthFactorOfShadow = new BigDecimal( "2" );
        BigDecimal doubleCloseOpenDeltaAbsolute = closeOpenDeltaAbsolute_BoxHeight.multiply( requiredMinimumLengthFactorOfShadow );
        Boolean hammer = ( lowerShadowHeight.compareTo( doubleCloseOpenDeltaAbsolute ) > 0 );
        return hammer;

    }


    // ---------|  Accessors  |------------------------
    // All fields are read-only. Just getters, no setters.
    public LocalDate getDate () {
        return this.date;
    }

    public BigDecimal getOpen () {
        return this.open;
    }

    public BigDecimal getHigh () {
        return this.high;
    }

    public BigDecimal getLow () {
        return this.low;
    }

    public BigDecimal getClose () {
        return this.close;
    }

    public Boolean isHammer () {
        return this.isHammer;
    }

    // ---------|  Override `Object`  |------------------------
    @Override
    public String toString () {
        return "StockDay{ " +
                "date=" + this.date +
                ", open=" + this.open +
                ", high=" + this.high +
                ", low=" + this.low +
                ", close=" + this.close +
                ", isHammer=" + this.isHammer +
                " }";
    }

    @Override
    public boolean equals ( final Object oArg ) {
        if ( this == oArg ) return true;
        if ( oArg == null || getClass() != oArg.getClass() ) return false;
        final StockDay stockDay = ( StockDay ) oArg;
        return Objects.equals( this.date , stockDay.date ) &&
                Objects.equals( this.open , stockDay.open ) &&
                Objects.equals( this.high , stockDay.high ) &&
                Objects.equals( this.low , stockDay.low ) &&
                Objects.equals( this.close , stockDay.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }

    @Override
    public int hashCode () {
        return Objects.hash( this.date , this.open , this.high , this.low , this.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }


    @Override
    public int compareTo ( final StockDay o ) {
        // Compare the date field only.
        int result = this.getDate().compareTo( o.getDate() );
        return result;
    }
}
package com.basilbourque.example;
导入java.math.BigDecimal;
导入java.time.LocalDate;
导入java.util.Objects;
公共类StockDay实现可比较的{
//示例数据:
//日期开盘高低点收盘
// 31/10/2016 58.25 58.65 58.2 58.35
//---------成员|------------------------
私有本地日期;
私有大十进制开、高、低、关;
私人布尔伊沙默;
//---------施工人员|------------------------
公开股票日(最终LocalDate localDateArg、最终BigDecimal openArg、最终BigDecimal highArg、最终BigDecimal lowArg、最终BigDecimal closeArg){
//在实际工作中,添加防御性代码来验证数据,例如没有空值、只有正数、合理的日期。
this.date=localDateArg;
this.open=openArg;
this.high=高arg;
this.low=低arg;
//确认高电平大于或等于低电平。
如果(此高)与(此低)相比<0{
抛出新的IllegalArgumentException(“对于“+this.Date+”的日期,通过的高点低于通过的低点。不可能”);
}
this.close=closeArg;
this.isHammer=this.determinatehammer();
}
私有布尔决定符(){
//锤子是烛台图表中的一种价格模式,当证券交易明显低于开盘价,但在当天晚些时候反弹,以高于或接近开盘价收盘时,就会出现这种模式。这种模式形成锤子形烛台,其主体至少有尾部或灯芯的一半大。
//阅读更多:锤子https://www.investopedia.com/terms/h/hammer.asp#ixzz5G6rqtbkv
//另见:http://www.onlinetradingconcepts.com/TechnicalAnalysis/Candlesticks/Hammer.html
//警告:此代码是一个快速的草稿,没有经过深思熟虑,并且完全未经测试。使用风险自负。仅用于演示目的。
//首先检查高点是否在收盘点上方。锤子上的“阴影”很少或没有(线在盒子上方突出)。为了简单起见,我们选择“无阴影”。
如果(this.high.compareTo(this.close)>0{//if high>close,不是锤子。
返回Boolean.FALSE;
}
//继续下一步检查:是否“尾部”(下部阴影)至少是长方体高度的两倍。
大分贝
if ( 
    ( ! stockDay.getDate().isBefore( start ) )     // A shorter way to ask "is equal to OR later" is "is NOT before". 
    && 
    stockDay.getDate().isBefore( stop )            // Half-Open approach, where beginning is *inclusive* while the ending is *exclusive*.
) { … }
if ( 
     LocalDateRange.of( start , stop )
                   .contains( LocalDate.parse( "2018-10-27" ) )
) { … }
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu" ) ;
LocalDate ld = LocalDate.parse( "31/10/2016" , f ) ;
package com.basilbourque.example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Objects;

public class StockDay implements Comparable < StockDay > {

    // Example data:
    // Date       Open  High  Low  Close
    // 31/10/2016 58.25 58.65 58.2 58.35

    // ---------|  Members  |------------------------
    private LocalDate date;
    private BigDecimal open, high, low, close;
    private Boolean isHammer;

    // ---------|  Constructors  |------------------------
    public StockDay ( final LocalDate localDateArg , final BigDecimal openArg , final BigDecimal highArg , final BigDecimal lowArg , final BigDecimal closeArg ) {
        // In real work, add defensive code to validate data such as no nulls, only positive numbers, reasonable dates.
        this.date = localDateArg;
        this.open = openArg;
        this.high = highArg;
        this.low = lowArg;
        // Verify the high is greater than or equal to the low.
        if ( this.high.compareTo( this.low ) < 0 ) {
            throw new IllegalArgumentException( "The passed High is below the passed Low for Date of " + this.date + ". Not possible." );
        }
        this.close = closeArg;
        this.isHammer = this.determineHammer();
    }

    private Boolean determineHammer () {
        // A hammer is a price pattern in candlestick charting that occurs when a security trades significantly lower than its opening, but rallies later in the day to close either above or near its opening price. This pattern forms a hammer-shaped candlestick, in which the body is at least half the size of the tail or wick.
        // Read more: Hammer https://www.investopedia.com/terms/h/hammer.asp#ixzz5G6rqtbkv
        // See also: http://www.onlinetradingconcepts.com/TechnicalAnalysis/Candlesticks/Hammer.html

        // Caveat: This code is a quick rough draft, not thought-through, and totally untested. Use at your own risk. For demonstration purposes only.

        // First check if the High is above the Close. A Hammer has little or no upper  "shadow" (line protruding above the box). We'll go with "no shadow" for simplicity here.
        if ( this.high.compareTo( this.close ) > 0 ) { // if high > close, not a hammer.
            return Boolean.FALSE;
        }

        // Proceed with next check: Is "tail" (lower shadow) at least twice as long as height of box.
        BigDecimal closeOpenDeltaAbsolute_BoxHeight = this.close.subtract( this.open ).abs();
        BigDecimal lowerOfCloseOrOpen = ( this.close.compareTo( this.open ) < 0 ) ? this.close : this.open;  // If x is smaller than y, use x. If x is greater than or equal to y, use y.
        BigDecimal lowerShadowHeight = lowerOfCloseOrOpen.subtract( this.low );
        // A Hammer has a long lower shadow (delta between either Close or Open, whichever is lower, and the Low), at least twice as tall as the box (Close-Open absolute delta).
        BigDecimal requiredMinimumLengthFactorOfShadow = new BigDecimal( "2" );
        BigDecimal doubleCloseOpenDeltaAbsolute = closeOpenDeltaAbsolute_BoxHeight.multiply( requiredMinimumLengthFactorOfShadow );
        Boolean hammer = ( lowerShadowHeight.compareTo( doubleCloseOpenDeltaAbsolute ) > 0 );
        return hammer;

    }


    // ---------|  Accessors  |------------------------
    // All fields are read-only. Just getters, no setters.
    public LocalDate getDate () {
        return this.date;
    }

    public BigDecimal getOpen () {
        return this.open;
    }

    public BigDecimal getHigh () {
        return this.high;
    }

    public BigDecimal getLow () {
        return this.low;
    }

    public BigDecimal getClose () {
        return this.close;
    }

    public Boolean isHammer () {
        return this.isHammer;
    }

    // ---------|  Override `Object`  |------------------------
    @Override
    public String toString () {
        return "StockDay{ " +
                "date=" + this.date +
                ", open=" + this.open +
                ", high=" + this.high +
                ", low=" + this.low +
                ", close=" + this.close +
                ", isHammer=" + this.isHammer +
                " }";
    }

    @Override
    public boolean equals ( final Object oArg ) {
        if ( this == oArg ) return true;
        if ( oArg == null || getClass() != oArg.getClass() ) return false;
        final StockDay stockDay = ( StockDay ) oArg;
        return Objects.equals( this.date , stockDay.date ) &&
                Objects.equals( this.open , stockDay.open ) &&
                Objects.equals( this.high , stockDay.high ) &&
                Objects.equals( this.low , stockDay.low ) &&
                Objects.equals( this.close , stockDay.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }

    @Override
    public int hashCode () {
        return Objects.hash( this.date , this.open , this.high , this.low , this.close );
        // Perhaps this should be coded to only consider the `LocalDate` field alone.
    }


    @Override
    public int compareTo ( final StockDay o ) {
        // Compare the date field only.
        int result = this.getDate().compareTo( o.getDate() );
        return result;
    }
}
package com.basilbourque.example;

import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class StockDayLoader {
    final private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );  // Tip: Instead of this custom format, use standard ISO 8601 formats when exchanging date-time values as text.

    public List < StockDay > loadFrom ( final Reader input ) {
        final List < StockDay > stockDays = new ArrayList <>();  // To be populated by lines of data read from the input.

        try (  // try-with-resources
               Scanner scanner = new Scanner( input ).useDelimiter( "\r\n" ) ; // Delimiter is a comma between fields.
        ) {
            scanner.useLocale( Locale.US ); // Determines cultural norms such as FULL STOP versus COMMA for decimal point in a `BigDecimal`.

            // Skip first line, the column headers.
            if ( scanner.hasNextLine() ) {
                String headers = scanner.nextLine(); // Ignore returned String.
                if ( ! "Date,Open,High,Low,Close".equals( headers ) ) { // Verify expected input.
                    throw new IllegalArgumentException( "The passed Readable object’s first row does not consist of expected column header names." );
                }
            }

            while ( scanner.hasNextLine() ) {
                String line = scanner.nextLine(); // Grab entire line.
                try (
                        Scanner lineScanner = new Scanner( line ).useDelimiter( "," ).useLocale( Locale.US ) ;
                ) {
                    String dateInput = lineScanner.next();
                    LocalDate date = LocalDate.parse( dateInput , this.dateFormatter );
                    try {
                        BigDecimal open = lineScanner.nextBigDecimal();
                        BigDecimal high = lineScanner.nextBigDecimal();
                        BigDecimal low = lineScanner.nextBigDecimal();
                        BigDecimal close = lineScanner.nextBigDecimal();
                        StockDay stockDay = new StockDay( date , open , high , low , close );
                        stockDays.add( stockDay );  // Collect the newly intanstiated `StockDay` object.
                    } catch ( InputMismatchException e ) {
                        System.out.println( "ERROR The next token does not match the Decimal regular expression, or is out of range.  " );
                        e.printStackTrace();
                    }
                }
            }

            return stockDays;
        }
    }


    // -----------|  Testing/Demo  |------------------------

    public static String bogusData () {
        final String eol = "\r\n";   // RFC 4180 requires CrLf as end-of-line.
        final StringBuilder sb = new StringBuilder();
        sb.append( "Date,Open,High,Low,Close" + eol );
        sb.append( "31/10/2016,58.25,58.65,58.2,58.35" + eol );
        sb.append( "28/10/2016,58.95,59,58.3,58.35" + eol );
        sb.append( "27/10/2016,58.78,58.22,33.3,58.55" + eol ); // Hammer.
        sb.append( "26/10/2016,58.95,59.05,58.43,58.45" + eol );
        sb.append( "25/10/2016,58.99,58.44,22.2,58.57" + eol ); // Hammer.
        String s = sb.toString();
        return s;
    }

    public static void main ( String[] args ) {
        String s = StockDayLoader.bogusData();
        Reader reader = new StringReader( s );

        StockDayLoader loader = new StockDayLoader();
        List < StockDay > list = loader.loadFrom( reader );

        System.out.println( list );
    }

}
package com.basilbourque.example;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

public class StockDayLoaderEasy {

    final private DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern( "dd/MM/uuuu" );  // Tip: Instead of this custom format, use standard ISO 8601 formats when exchanging date-time values as text.

    public List < StockDay > loadFrom ( final Reader reader ) {
        final List < StockDay > stockDays = new ArrayList <>();  // To be populated by lines of data read from the input.

        Iterable < CSVRecord > records = null;
        try {
            records = CSVFormat.RFC4180.parse( reader );
        } catch ( IOException eArg ) {
            eArg.printStackTrace();
        }

        // Read each column. Names:  "Date,Open,High,Low,Close"
        for ( CSVRecord record : records ) {
            LocalDate date = LocalDate.parse( record.get( "Date" ) , this.dateFormatter );
            BigDecimal open = new BigDecimal( record.get( "Open" ) );
            BigDecimal high = new BigDecimal( record.get( "High" ) );
            BigDecimal low = new BigDecimal( record.get( "Low" ) );
            BigDecimal close = new BigDecimal( record.get( "Close" ) );

            StockDay stockDay = new StockDay( date , open , high , low , close );
            stockDays.add( stockDay );  // Collect the newly intanstiated `StockDay` object.
        }
        return stockDays;
    }

    // -----------|  Testing/Demo  |------------------------

    public static String bogusData () {
        final String eol = "\r\n";   // RFC 4180 requires CrLf as end-of-line.
        final StringBuilder sb = new StringBuilder();
        sb.append( "Date,Open,High,Low,Close" + eol );
        sb.append( "31/10/2016,58.25,58.65,58.2,58.35" + eol );
        sb.append( "28/10/2016,58.95,59,58.3,58.35" + eol );
        sb.append( "27/10/2016,58.78,58.22,33.3,58.55" + eol ); // Hammer.
        sb.append( "26/10/2016,58.95,59.05,58.43,58.45" + eol );
        sb.append( "25/10/2016,58.99,58.44,22.2,58.57" + eol ); // Hammer.
        String s = sb.toString();
        return s;
    }

    public static void main ( String[] args ) {
        String s = StockDayLoader.bogusData();
        Reader reader = new StringReader( s );

        StockDayLoader loader = new StockDayLoader();
        List < StockDay > list = loader.loadFrom( reader );

        System.out.println( list );
    }
}
package com.basilbourque.example;

import org.threeten.extra.LocalDateRange;

import java.io.Reader;
import java.io.StringReader;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class HammerTime {
    public static void main ( String[] args ) {
        HammerTime hammerTime = new HammerTime();
        hammerTime.doIt();
    }

    private void doIt () {
        // Load all data.
        Reader reader = new StringReader( StockDayLoader.bogusData() );
        List < StockDay > stockDays = new StockDayLoader().loadFrom( reader );
        Collections.sort( stockDays );  // Sort chronologically, ascending order, oldest first. For newest first, call `Collections.reverse`.
        System.out.println( "All stockDays = " + stockDays );

        // Find hammers using the old-fashioned way.
        List < StockDay > hammers = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                hammers.add( stockDay );
            }
        }
        System.out.println( "hammers: " + hammers );

        // Find hammers using modern Streams/Lambda features, while tolerating NULLs.
        List < StockDay > hammers2 = stockDays.stream()
                .filter( stockDay -> Objects.equals( stockDay.isHammer() , Boolean.TRUE ) )  // Use `Objects.equals` to tolerate NULL values.
                .collect( Collectors.toList() );
        System.out.println( "hammers2: " + hammers2 );

        // Find hammers using modern Streams/Lambda features, assuming no NULL values exist.
        List < StockDay > hammers3 = stockDays.stream()
                .filter( stockDay -> stockDay.isHammer().equals( Boolean.TRUE ) )  // Simpler syntax than above, if you are certain of no NULLs.
                .collect( Collectors.toList() );
        System.out.println( "hammers3: " + hammers3 );

        // Find hammers within a certain date-range, per the original Question.

        // Parse the user’s input start/stop dates.
        LocalDate start = LocalDate.parse( "2016-10-26" ); // This range should pick up the hammer on the 27th while omitting the hammer on the 25th.
        LocalDate stop = LocalDate.parse( "2016-10-28" ); // Usual practice in defining a span-of-time is the Half-Open approach, where the beginning is *inclusive* while the ending is *exclusive*.

        // Find hammers within date range using the old-fashioned syntax, with built-in classes.
        List < StockDay > hammersInDateRange1 = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                if ( ( ! stockDay.getDate().isBefore( start ) ) && stockDay.getDate().isBefore( stop ) ) {
                    hammersInDateRange1.add( stockDay );
                }
            }
        }
        System.out.println( "hammersInDateRange1: " + hammersInDateRange1 );

        // Find hammers within date range using the old-fashioned syntax, with the ThreeTen-Extra library and its `LocalDateRange` class.  http://www.threeten.org/threeten-extra/
        final LocalDateRange dateRange = LocalDateRange.of( start , stop );
        List < StockDay > hammersInDateRange2 = new ArrayList <>();
        for ( StockDay stockDay : stockDays ) {
            if ( stockDay.isHammer() ) {
                if ( dateRange.contains( stockDay.getDate() ) ) {
                    hammersInDateRange2.add( stockDay );
                }
            }
        }
        System.out.println( "hammersInDateRange2: " + hammersInDateRange2 );

        // Find hammers within date range using modern Streams/Lambda syntax, with the ThreeTen-Extra library and its `LocalDateRange` class.  http://www.threeten.org/threeten-extra/
        List < StockDay > hammersInDateRange3 = stockDays.stream()
                .filter( stockDay -> stockDay.isHammer() && dateRange.contains( stockDay.getDate() ) )  // Assumes no NULLs.
                .collect( Collectors.toList() );
        System.out.println( "hammersInDateRange3: " + hammersInDateRange3 );


    }
}